Implement add transaction
This commit is contained in:
parent
ed52f9a34e
commit
84bd7f66d9
@ -10,19 +10,11 @@
|
||||
<cargoProject FILE="$PROJECT_DIR$/bookkeeper/Cargo.toml" />
|
||||
</component>
|
||||
<component name="ChangeListManager">
|
||||
<list default="true" id="76b3b902-7a5c-4bcd-9c1b-8241d748fb44" name="更改" comment="Add index page">
|
||||
<change afterPath="$PROJECT_DIR$/.idea/jsLibraryMappings.xml" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/bookkeeper/templates/nav.html.j2" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/bookkeeper/templates/tx.html.j2" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/.idea/finance-consumer.iml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/finance-consumer.iml" afterDir="false" />
|
||||
<list default="true" id="76b3b902-7a5c-4bcd-9c1b-8241d748fb44" name="更改" comment="Refine data structure and index page">
|
||||
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/bookkeeper/Cargo.lock" beforeDir="false" afterPath="$PROJECT_DIR$/bookkeeper/Cargo.lock" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/bookkeeper/Cargo.toml" beforeDir="false" afterPath="$PROJECT_DIR$/bookkeeper/Cargo.toml" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/bookkeeper/entity/src/transaction.rs" beforeDir="false" afterPath="$PROJECT_DIR$/bookkeeper/entity/src/transaction.rs" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/bookkeeper/migration/src/m20220101_000001_create_table.rs" beforeDir="false" afterPath="$PROJECT_DIR$/bookkeeper/migration/src/m20220101_000001_create_table.rs" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/bookkeeper/src/main.rs" beforeDir="false" afterPath="$PROJECT_DIR$/bookkeeper/src/main.rs" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/bookkeeper/templates/base.html.j2" beforeDir="false" afterPath="$PROJECT_DIR$/bookkeeper/templates/base.html.j2" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/bookkeeper/templates/index.html.j2" beforeDir="false" afterPath="$PROJECT_DIR$/bookkeeper/templates/index.html.j2" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/bookkeeper/templates/tx.html.j2" beforeDir="false" afterPath="$PROJECT_DIR$/bookkeeper/templates/tx.html.j2" afterDir="false" />
|
||||
</list>
|
||||
<option name="SHOW_DIALOG" value="false" />
|
||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||
@ -44,7 +36,7 @@
|
||||
<option name="directoryName" value="JuSRXtPJ" />
|
||||
</component>
|
||||
<component name="ProblemsViewState">
|
||||
<option name="selectedTabId" value="ProjectErrors" />
|
||||
<option name="selectedTabId" value="CurrentFile" />
|
||||
</component>
|
||||
<component name="ProjectColorInfo">{
|
||||
"customColor": "",
|
||||
@ -157,7 +149,7 @@
|
||||
<workItem from="1735391132866" duration="1139000" />
|
||||
<workItem from="1735392290940" duration="620000" />
|
||||
<workItem from="1735392928494" duration="12910000" />
|
||||
<workItem from="1735470376833" duration="8472000" />
|
||||
<workItem from="1735470376833" duration="15697000" />
|
||||
</task>
|
||||
<task id="LOCAL-00001" summary="Add bookkeeper">
|
||||
<option name="closed" value="true" />
|
||||
@ -191,7 +183,15 @@
|
||||
<option name="project" value="LOCAL" />
|
||||
<updated>1735406260589</updated>
|
||||
</task>
|
||||
<option name="localTasksCounter" value="5" />
|
||||
<task id="LOCAL-00005" summary="Refine data structure and index page">
|
||||
<option name="closed" value="true" />
|
||||
<created>1735479514256</created>
|
||||
<option name="number" value="00005" />
|
||||
<option name="presentableId" value="LOCAL-00005" />
|
||||
<option name="project" value="LOCAL" />
|
||||
<updated>1735479514256</updated>
|
||||
</task>
|
||||
<option name="localTasksCounter" value="6" />
|
||||
<servers />
|
||||
</component>
|
||||
<component name="TypeScriptGeneratedFilesManager">
|
||||
@ -213,7 +213,8 @@
|
||||
<MESSAGE value="Insert transaction" />
|
||||
<MESSAGE value="Format main.rs" />
|
||||
<MESSAGE value="Add index page" />
|
||||
<option name="LAST_COMMIT_MESSAGE" value="Add index page" />
|
||||
<MESSAGE value="Refine data structure and index page" />
|
||||
<option name="LAST_COMMIT_MESSAGE" value="Refine data structure and index page" />
|
||||
</component>
|
||||
<component name="XSLT-Support.FileAssociations.UIState">
|
||||
<expand />
|
||||
|
||||
@ -69,7 +69,7 @@ async fn index(conn: Connection<'_, Db>) -> Template {
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(FromForm)]
|
||||
#[derive(FromForm, Debug)]
|
||||
struct TransModel<'r> {
|
||||
code: usize,
|
||||
name: &'r str,
|
||||
@ -78,10 +78,10 @@ struct TransModel<'r> {
|
||||
volume: i32,
|
||||
net_buy: &'r str,
|
||||
date: &'r str,
|
||||
split_pieces: Vec<SplitPieceForm<'r>>,
|
||||
split_pieces: Option<Vec<SplitPieceForm<'r>>>,
|
||||
}
|
||||
|
||||
#[derive(FromForm, Serialize)]
|
||||
#[derive(FromForm, Serialize, Debug)]
|
||||
struct SplitPieceForm<'r> {
|
||||
date: &'r str,
|
||||
sell: &'r str,
|
||||
@ -98,8 +98,17 @@ struct SplitPiece{
|
||||
volume: i32,
|
||||
}
|
||||
|
||||
#[get("/add")]
|
||||
async fn get_add() -> Template {
|
||||
Template::render("tx", context! {
|
||||
r: (),
|
||||
target: "/add",
|
||||
})
|
||||
}
|
||||
|
||||
#[post("/add", data = "<trans>")]
|
||||
async fn add(trans: Form<TransModel<'_>>, conn: Connection<'_, Db>) -> Flash<Redirect> {
|
||||
dbg!(&trans);
|
||||
let record = translate(trans);
|
||||
|
||||
record.insert(conn.into_inner()).await.unwrap();
|
||||
@ -120,9 +129,9 @@ fn translate(trans: Form<TransModel>) -> transaction::ActiveModel {
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
if trans.split_pieces.len() > 0 {
|
||||
if let Some(split_pieces) = &trans.split_pieces {
|
||||
let mut sp: Vec<SplitPiece> = Vec::new();
|
||||
for split_piece in trans.split_pieces.iter() {
|
||||
for split_piece in split_pieces.iter() {
|
||||
sp.push(SplitPiece{
|
||||
date: NaiveDate::parse_from_str(split_piece.date, "%Y-%m-%d").unwrap(),
|
||||
sell: Decimal::from_str(split_piece.sell).unwrap(),
|
||||
@ -165,5 +174,5 @@ async fn rocket() -> _ {
|
||||
.attach(Db::init())
|
||||
.attach(AdHoc::try_on_ignite("Migrations", run_migrations))
|
||||
.attach(Template::fairing())
|
||||
.mount("/", routes![index, add])
|
||||
.mount("/", routes![index, add, get_add])
|
||||
}
|
||||
|
||||
@ -2,18 +2,18 @@
|
||||
{% block title %}交易{% endblock title %}
|
||||
{% block container %}
|
||||
<div class="">
|
||||
<table class="table is-fullwidth is-striped is-bordered">
|
||||
<table class="table is-fullwidth is-bordered is-hoverable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>代码</th>
|
||||
<th>名称</th>
|
||||
<th style="width: fit-content">代码</th>
|
||||
<th style="width: fit-content">名称</th>
|
||||
<th>买入日期</th>
|
||||
<th>买入价</th>
|
||||
<th>成交量</th>
|
||||
<th>买量</th>
|
||||
<th><abbr title="买入金额+佣金+过户费等各种手续费">实际买入</abbr></th>
|
||||
<th>卖出日期</th>
|
||||
<th>卖出价</th>
|
||||
<th>成交量</th>
|
||||
<th>卖量</th>
|
||||
<th><abbr title="卖出金额-佣金-各种手续费">实际卖出</abbr></th>
|
||||
<th>收益</th>
|
||||
<th>实际收益</th>
|
||||
@ -24,11 +24,11 @@
|
||||
{% for r in rows %}
|
||||
{% if r.split_pieces %}
|
||||
{% for p in r.split_pieces %}
|
||||
<tr class="{% if r.is_done %}{% if r.net_gain|float > 0 %}is-success{% else %}is-danger{% endif %}{% else %}is-warning{% endif %}">
|
||||
<tr>
|
||||
{% if loop.index == 1 %}
|
||||
<td rowspan="{{ loop.length }}" style="vertical-align: middle;">{{ r.code }}</td>
|
||||
<td rowspan="{{ loop.length }}" style="vertical-align: middle;">{{ r.name }}</td>
|
||||
<td rowspan="{{ loop.length }}" style="vertical-align: middle;">{{ r.date }}</td>
|
||||
<td rowspan="{{ loop.length }}" style="vertical-align: middle;" class="{% if not r.is_done %}is-warning{% endif %}">{{ r.date }}</td>
|
||||
<td rowspan="{{ loop.length }}" style="vertical-align: middle;">{{ r.buy }}</td>
|
||||
<td rowspan="{{ loop.length }}" style="vertical-align: middle;">{{ r.volume }}</td>
|
||||
<td rowspan="{{ loop.length }}" style="vertical-align: middle;">{{ r.net_buy }}</td>
|
||||
@ -36,8 +36,8 @@
|
||||
<td>{{ p.sell or "" }}</td>
|
||||
<td>{{ p.volume or "" }}</td>
|
||||
<td>{{ p.net_sell or "" }}</td>
|
||||
<td rowspan="{{ loop.length }}" style="vertical-align: middle;">{{ (r.gain or 0)|float|round(4) }}%</td>
|
||||
<td rowspan="{{ loop.length }}" style="vertical-align: middle;">{{ (r.net_gain or 0)|float|round(4) }}%</td>
|
||||
<td rowspan="{{ loop.length }}" style="vertical-align: middle;" class="{% if r.gain|float > 0 %}is-success{% else %}is-danger{% endif %}">{{ (r.gain or 0)|float|round(4) }}%</td>
|
||||
<td rowspan="{{ loop.length }}" style="vertical-align: middle;" class="{% if r.net_gain|float > 0 %}is-success{% else %}is-danger{% endif %}">{{ (r.net_gain or 0)|float|round(4) }}%</td>
|
||||
<td rowspan="{{ loop.length }}" style="vertical-align: middle;">
|
||||
<a href="#">修改</a>
|
||||
</td>
|
||||
@ -50,10 +50,10 @@
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<tr class="is-warning">
|
||||
<tr>
|
||||
<td>{{ r.code }}</td>
|
||||
<td>{{ r.name }}</td>
|
||||
<td>{{ r.date }}</td>
|
||||
<td class="is-warning">{{ r.date }}</td>
|
||||
<td>{{ r.buy }}</td>
|
||||
<td>{{ r.volume }}</td>
|
||||
<td>{{ r.net_buy }}</td>
|
||||
@ -74,4 +74,80 @@
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="modal" id="modal">
|
||||
<div class="modal-background"></div>
|
||||
<div class="modal-content" id="modal-content" style="width: 50rem;">
|
||||
<!-- Any other Bulma elements you want -->
|
||||
</div>
|
||||
<button class="modal-close is-large" aria-label="close"></button>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
// Functions to open and close a modal
|
||||
function openModal($el) {
|
||||
$el.classList.add('is-active');
|
||||
}
|
||||
|
||||
function closeModal($el) {
|
||||
$el.classList.remove('is-active');
|
||||
}
|
||||
|
||||
function closeAllModals() {
|
||||
(document.querySelectorAll('.modal') || []).forEach(($modal) => {
|
||||
closeModal($modal);
|
||||
});
|
||||
}
|
||||
|
||||
document.getElementById('addEntry').addEventListener('click', async () => {
|
||||
const $target = document.getElementById("modal");
|
||||
const response = await fetch("/add");
|
||||
if (!response.ok) {
|
||||
return;
|
||||
}
|
||||
document.getElementById('modal-content').innerHTML = await response.text();
|
||||
openModal($target);
|
||||
});
|
||||
|
||||
// Add a click event on various child elements to close the parent modal
|
||||
(document.querySelectorAll('.modal-background, .modal-close, .modal-card-head .delete, .modal-card-foot .button') || []).forEach(($close) => {
|
||||
const $target = $close.closest('.modal');
|
||||
|
||||
$close.addEventListener('click', () => {
|
||||
closeModal($target);
|
||||
});
|
||||
});
|
||||
|
||||
// Add a keyboard event to close all modals
|
||||
document.addEventListener('keydown', (event) => {
|
||||
if (event.key === "Escape") {
|
||||
closeAllModals();
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
function addRow() {
|
||||
const this_row = document.getElementById('add-row-line');
|
||||
this_row.parentElement.insertBefore(this_row.previousElementSibling.cloneNode(true), this_row);
|
||||
for (let i = 0; i < this_row.parentElement.childElementCount-1; i++) {
|
||||
let row = this_row.parentElement.children[i];
|
||||
row.children[0].children[0].name = `split_pieces[${i}].date`;
|
||||
row.children[1].children[0].name = `split_pieces[${i}].sell`;
|
||||
row.children[2].children[0].name = `split_pieces[${i}].volume`;
|
||||
row.children[3].children[0].name = `split_pieces[${i}].net_sell`;
|
||||
}
|
||||
}
|
||||
|
||||
function deleteRow(el) {
|
||||
const this_row = el.parentElement.parentElement; // tr
|
||||
const first_data_row = this_row.parentElement.firstElementChild;
|
||||
const last_data_row = this_row.parentElement.lastElementChild.previousElementSibling; // last is add button
|
||||
if (first_data_row === last_data_row) {
|
||||
console.log("The only row");
|
||||
} else {
|
||||
this_row.parentElement.removeChild(this_row);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
{% endblock container %}
|
||||
|
||||
@ -1,61 +1,76 @@
|
||||
<div class="">
|
||||
<table class="table is-fullwidth is-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>代码</th>
|
||||
<th>名称</th>
|
||||
<th>买入日期</th>
|
||||
<th>买入价</th>
|
||||
<th>成交量</th>
|
||||
<th><abbr title="买入价+佣金+过户费等各种手续费">实际买入价</abbr></th>
|
||||
<th>卖出日期</th>
|
||||
<th>卖出价</th>
|
||||
<th>成交量</th>
|
||||
<th><abbr title="卖出价-佣金-各种手续费">实际卖出价</abbr></th>
|
||||
<th>收益</th>
|
||||
<th>实际收益</th>
|
||||
<th><abbr title="已完成">O</abbr></th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for r in rows %}
|
||||
<tr>
|
||||
<td>{{ r.code }}</td>
|
||||
<td>{{ r.name }}</td>
|
||||
<td>{{ r.date }}</td>
|
||||
<td>{{ r.buy }}</td>
|
||||
<td>{{ r.volume }}</td>
|
||||
<td>{{ r.net_buy }}</td>
|
||||
{% if r.split_pieces %}
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td>}</td>
|
||||
<td></td>
|
||||
{% else %}
|
||||
<td>{{ r.sell_date or "" }}</td>
|
||||
<td>{{ r.sell or "" }}</td>
|
||||
<td>{{ r.sold_volume or "" }}</td>
|
||||
<td>{{ r.net_sell or "" }}</td>
|
||||
{% endif %}
|
||||
<td>{{ r.gain or "" }}</td>
|
||||
<td>{{ r.net_gain or "" }}</td>
|
||||
<td>
|
||||
{% if r.sold_volume == r.volume %}
|
||||
✅
|
||||
<div class="box">
|
||||
<form action="{{ target }}" method="post">
|
||||
<div class="field">
|
||||
<label class="label" for="code">代码</label>
|
||||
<div class="control">
|
||||
<input id="code" name="code" class="input" type="number" placeholder="Code" value="{{ r.code or 0 }}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label" for="name">名称</label>
|
||||
<div class="control">
|
||||
<input id="name" name="name" class="input" type="text" placeholder="Name" value="{{ r.name or '' }}">
|
||||
</div>
|
||||
</div>
|
||||
<table class="table is-fullwidth is-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><label class="label" for="date">买入日期</label></th>
|
||||
<th><label class="label" for="buy">买入价</label></th>
|
||||
<th><label class="label" for="volume">成交量(买)</label></th>
|
||||
<th><label class="label" for="net_buy">实际买入</label></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><input id="date" name="date" class="input" type="date" placeholder="YYYY-MM-DD" value="{{ r.date or '' }}"></td>
|
||||
<td><input id="buy" name="buy" class="input" type="number" placeholder="Buy" value="{{ r.buy or 0 }}"></td>
|
||||
<td><input id="volume" name="volume" class="input" type="number" placeholder="Volume" value="{{ r.volume or 0 }}"></td>
|
||||
<td><input id="net_buy" name="net_buy" class="input" type="number" placeholder="Net Buy" value="{{ r.net_buy or 0 }}"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<table class="table is-fullwidth is-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><label class="label">卖出日期</label></th>
|
||||
<th><label class="label">买入价</label></th>
|
||||
<th><label class="label">成交量</label></th>
|
||||
<th><label class="label">实际买入</label></th>
|
||||
<th style="min-width: 4rem; vertical-align: middle;">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
{% if r.split_pieces %}
|
||||
{% for split in r.split_pieces %}
|
||||
<td><input name="split_pieces[].date" class="input" type="date" placeholder="YYYY-MM-DD" value="{{ split.date or '' }}"></td>
|
||||
<td><input name="split_pieces[].sell" class="input" type="number" placeholder="Sell" value="{{ split.sell or '' }}"></td>
|
||||
<td><input name="split_pieces[].volume" class="input" type="number" placeholder="Volume" value="{{ split.volume or '' }}"></td>
|
||||
<td><input name="split_pieces[].net_sell" class="input" type="number" placeholder="Net Sell" value="{{ split.net_sell or '' }}"></td>
|
||||
<td><a onclick="deleteRow(this)">删除</a></td>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
☐
|
||||
<td><input name="split_pieces[0].date" class="input" type="date" placeholder="YYYY-MM-DD"></td>
|
||||
<td><input name="split_pieces[0].sell" class="input" type="number" placeholder="Sell"></td>
|
||||
<td><input name="split_pieces[0].volume" class="input" type="number" placeholder="Volume"></td>
|
||||
<td><input name="split_pieces[0].net_sell" class="input" type="number" placeholder="Net Sell"></td>
|
||||
<td style="vertical-align: middle;"><a onclick="deleteRow(this)">删除</a></td>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
<a href="#">修改</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
<tr>
|
||||
<td colspan="14" style="text-align: center"><a id="addEntry">添加</a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</tr>
|
||||
<tr class="is-light" id="add-row-line">
|
||||
<td colspan="5" style="text-align: center"><a onclick="addRow()">添加</a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="field is-grouped">
|
||||
<div class="control">
|
||||
<button class="button is-link">Submit</button>
|
||||
</div>
|
||||
<div class="control">
|
||||
<button class="button is-link is-light">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user