Merge branch 'main' of g.8f.al:ir/budget

This commit is contained in:
zky2020 2023-06-05 22:33:52 +08:00
commit 446adc1155
5 changed files with 499 additions and 62 deletions

View file

@ -1,11 +1,10 @@
-- 账户表 -- 账户表
CREATE TABLE account ( CREATE TABLE account (
a_id SERIAL PRIMARY KEY, -- 账户号 a_id SERIAL PRIMARY KEY, -- 账户号
balance MONEY, -- 余额 balance MONEY, -- 余额
meta JSONB -- 元数据 meta JSONB -- 元数据
); );
-- {"name":"name", -- {"name":"name",
-- time:"2022-02-02",
-- “budget": [ -- “budget": [
-- {"time":"2022-02","value":20.32}, -- {"time":"2022-02","value":20.32},
-- {"time":"2022-01","value":30.32} -- {"time":"2022-01","value":30.32}
@ -14,8 +13,8 @@ CREATE TABLE account (
-- 类别表 -- 类别表
CREATE TABLE category ( CREATE TABLE category (
c_id SERIAL PRIMARY KEY, -- 类别号 c_id SERIAL PRIMARY KEY, -- 类别号
meta JSONB -- 元数据 meta JSONB -- 元数据
); );
-- {"name":"name", -- {"name":"name",
-- "type":"in"/"out", -- "type":"in"/"out",
@ -27,19 +26,18 @@ CREATE TABLE category (
-- 流水表 -- 流水表
CREATE TABLE transaction ( CREATE TABLE transaction (
t_id SERIAL PRIMARY KEY, -- 流水号 t_id SERIAL PRIMARY KEY, -- 流水号
a_id INTEGER REFERENCES account(a_id) NOT NULL, -- 关联账户号 a_id INTEGER REFERENCES account(a_id) NOT NULL, -- 关联账户号
c_id INTEGER REFERENCES category(c_id), -- 关联类别号 c_id INTEGER REFERENCES category(c_id), -- 关联类别号
s_id INTEGER REFERENCES category(c_id), -- 源账户号 s_id INTEGER REFERENCES account(a_id), -- 源账户号
time TIMESTAMP, time TIMESTAMP,
amount MONEY, amount MONEY,
meta JSONB -- 元数据 meta JSONB -- 元数据
); );
-- {"discription":"something",(可选) -- {"discription":"something",
-- "type":"in"/"out"/"transfer"/"init"/"modify", -- "type":"in"/"out"/"transfer"/"init",
-- "reimburse":{
-- "reimburse":{ (报销) -- "finish": true/false,
-- "finish": true,
-- "ref":t_id -- "ref":t_id
-- } -- }
-- "":"something"} -- "":"something"}

336
scripts/data.sql Normal file
View file

@ -0,0 +1,336 @@
--
-- PostgreSQL database dump
--
-- Dumped from database version 15.3 (Debian 15.3-1.pgdg110+1)
-- Dumped by pg_dump version 15.3
SET statement_timeout = 0;
SET lock_timeout = 0;
SET idle_in_transaction_session_timeout = 0;
SET client_encoding = 'UTF8';
SET standard_conforming_strings = on;
SELECT pg_catalog.set_config('search_path', '', false);
SET check_function_bodies = false;
SET xmloption = content;
SET client_min_messages = warning;
SET row_security = off;
SET default_tablespace = '';
SET default_table_access_method = heap;
--
-- Name: account; Type: TABLE; Schema: public; Owner: budget
--
CREATE TABLE public.account (
a_id integer NOT NULL,
balance money,
meta jsonb
);
ALTER TABLE public.account OWNER TO budget;
--
-- Name: account_a_id_seq; Type: SEQUENCE; Schema: public; Owner: budget
--
CREATE SEQUENCE public.account_a_id_seq
AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER TABLE public.account_a_id_seq OWNER TO budget;
--
-- Name: account_a_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: budget
--
ALTER SEQUENCE public.account_a_id_seq OWNED BY public.account.a_id;
--
-- Name: category; Type: TABLE; Schema: public; Owner: budget
--
CREATE TABLE public.category (
c_id integer NOT NULL,
meta jsonb
);
ALTER TABLE public.category OWNER TO budget;
--
-- Name: category_c_id_seq; Type: SEQUENCE; Schema: public; Owner: budget
--
CREATE SEQUENCE public.category_c_id_seq
AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER TABLE public.category_c_id_seq OWNER TO budget;
--
-- Name: category_c_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: budget
--
ALTER SEQUENCE public.category_c_id_seq OWNED BY public.category.c_id;
--
-- Name: transaction; Type: TABLE; Schema: public; Owner: budget
--
CREATE TABLE public.transaction (
t_id integer NOT NULL,
a_id integer NOT NULL,
c_id integer,
s_id integer,
"time" timestamp without time zone,
amount money,
meta jsonb
);
ALTER TABLE public.transaction OWNER TO budget;
--
-- Name: transaction_t_id_seq; Type: SEQUENCE; Schema: public; Owner: budget
--
CREATE SEQUENCE public.transaction_t_id_seq
AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER TABLE public.transaction_t_id_seq OWNER TO budget;
--
-- Name: transaction_t_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: budget
--
ALTER SEQUENCE public.transaction_t_id_seq OWNED BY public.transaction.t_id;
--
-- Name: account a_id; Type: DEFAULT; Schema: public; Owner: budget
--
ALTER TABLE ONLY public.account ALTER COLUMN a_id SET DEFAULT nextval('public.account_a_id_seq'::regclass);
--
-- Name: category c_id; Type: DEFAULT; Schema: public; Owner: budget
--
ALTER TABLE ONLY public.category ALTER COLUMN c_id SET DEFAULT nextval('public.category_c_id_seq'::regclass);
--
-- Name: transaction t_id; Type: DEFAULT; Schema: public; Owner: budget
--
ALTER TABLE ONLY public.transaction ALTER COLUMN t_id SET DEFAULT nextval('public.transaction_t_id_seq'::regclass);
--
-- Data for Name: account; Type: TABLE DATA; Schema: public; Owner: budget
--
COPY public.account (a_id, balance, meta) FROM stdin;
4 $13,040.00 {"name": "储蓄卡(建设银行)", "description": ""}
3 $19,250.00 {"name": "工资卡(中国银行)", "description": ""}
1 $3,214.43 {"name": "微信", "description": ""}
2 $544.10 {"name": "支付宝", "description": ""}
\.
--
-- Data for Name: category; Type: TABLE DATA; Schema: public; Owner: budget
--
COPY public.category (c_id, meta) FROM stdin;
1 {"name": "饮食", "type": "out", "description": ""}
2 {"name": "水电", "type": "out", "description": ""}
3 {"name": "娱乐", "type": "out", "description": ""}
4 {"name": "学习", "type": "out", "description": ""}
5 {"name": "工资", "type": "in", "description": ""}
6 {"name": "理财", "type": "in", "description": ""}
7 {"name": "水电", "type": "out", "description": "低消费"}
8 {"name": "医疗", "type": "out", "description": ""}
9 {"name": "交通", "type": "out", "description": ""}
10 {"name": "购物", "type": "out", "description": ""}
\.
--
-- Data for Name: transaction; Type: TABLE DATA; Schema: public; Owner: budget
--
COPY public.transaction (t_id, a_id, c_id, s_id, "time", amount, meta) FROM stdin;
5 1 1 \N 2023-06-04 14:14:52.541843 -$32.75 {"type": "out", "description": ""}
6 2 1 \N 2023-06-04 14:15:03.209135 -$73.20 {"type": "out", "description": ""}
7 1 1 \N 2023-06-04 14:15:14.321607 -$57.92 {"type": "out", "description": ""}
8 1 2 \N 2023-06-04 14:15:23.770381 -$235.00 {"type": "out", "description": ""}
1 1 \N \N 2023-02-04 14:13:06 $3,000.00 {"type": "init", "description": "Initial Balance"}
2 2 \N \N 2023-02-04 14:13:22 $600.00 {"type": "init", "description": "Initial Balance"}
3 3 \N \N 2023-02-04 14:13:34 $4,000.00 {"type": "init", "description": "Initial Balance"}
4 4 \N \N 2023-02-04 14:13:58 $7,000.00 {"type": "init", "description": "Initial Balance"}
38 2 7 \N 2023-05-07 19:55:43 -$60.00 {"type": "out", "description": ""}
39 2 4 \N 2023-05-06 19:55:59 -$60.00 {"type": "out", "description": ""}
9 1 2 \N 2023-05-15 14:15:31 -$235.00 {"type": "out", "description": "电费过多"}
10 2 3 \N 2023-06-04 14:36:54.760977 -$50.00 {"type": "out", "description": ""}
12 1 6 \N 2023-06-04 14:37:24.994996 $3,000.00 {"type": "in", "description": ""}
11 3 5 \N 2023-06-04 14:37:15 $5,000.00 {"type": "in", "description": "主要工资"}
13 1 1 \N 2023-06-03 19:11:36 -$200.00 {"type": "out", "description": ""}
14 1 2 \N 2023-06-02 19:12:13 -$20.00 {"type": "out", "description": ""}
15 2 3 \N 2023-06-01 19:12:49 -$300.00 {"type": "out", "description": ""}
16 2 4 \N 2023-05-04 19:14:01 -$20.00 {"type": "out", "description": ""}
17 4 5 \N 2023-05-04 19:14:45 $5,000.00 {"type": "in", "description": ""}
18 3 5 \N 2023-05-05 19:15:49 $5,000.00 {"type": "in", "description": ""}
21 2 6 \N 2023-05-04 19:18:04 $500.00 {"type": "in", "description": ""}
23 3 6 \N 2023-06-03 19:19:09 $300.00 {"type": "in", "description": ""}
24 4 6 \N 2023-05-03 19:19:39 $500.00 {"type": "in", "description": ""}
25 1 4 \N 2023-05-03 19:20:39 -$50.00 {"type": "out", "description": ""}
26 3 1 \N 2023-05-04 19:48:58 -$50.00 {"type": "out", "description": ""}
27 1 2 \N 2023-05-03 19:49:15 -$60.00 {"type": "out", "description": ""}
28 1 3 \N 2023-05-02 19:49:34 -$60.00 {"type": "out", "description": ""}
29 4 3 \N 2023-05-01 19:49:53 -$60.00 {"type": "out", "description": ""}
30 1 4 \N 2023-05-06 19:50:13 -$50.00 {"type": "out", "description": ""}
31 2 4 \N 2023-05-05 19:50:36 -$50.00 {"type": "out", "description": ""}
32 1 7 \N 2023-05-07 19:51:01 -$20.00 {"type": "out", "description": ""}
33 2 7 \N 2023-05-08 19:51:28 -$30.00 {"type": "out", "description": ""}
34 4 6 \N 2023-05-07 19:51:47 $600.00 {"type": "in", "description": ""}
35 2 1 \N 2023-05-07 19:53:29 -$60.00 {"type": "out", "description": ""}
36 2 3 \N 2023-05-04 19:53:44 -$80.00 {"type": "out", "description": ""}
37 1 4 \N 2023-05-04 19:53:59 -$50.00 {"type": "out", "description": ""}
40 1 1 \N 2023-05-01 19:56:13 -$300.00 {"type": "out", "description": ""}
41 1 3 \N 2023-05-03 19:56:40 -$60.00 {"type": "out", "description": ""}
42 1 1 \N 2023-05-04 19:56:58 -$500.00 {"type": "out", "description": ""}
43 1 4 \N 2023-05-08 19:57:13 -$50.00 {"type": "out", "description": ""}
44 1 1 \N 2023-05-08 19:57:35 -$50.00 {"type": "out", "description": ""}
45 1 2 \N 2023-05-08 19:57:47 -$50.00 {"type": "out", "description": ""}
46 2 3 \N 2023-05-08 19:58:54 -$60.00 {"type": "out", "description": ""}
47 1 6 \N 2023-05-08 19:59:13 $500.00 {"type": "in", "description": ""}
48 1 2 \N 2023-05-08 19:59:35 -$800.00 {"type": "out", "description": ""}
49 1 4 \N 2023-05-07 19:59:47 -$60.00 {"type": "out", "description": ""}
50 3 5 \N 2023-06-04 20:00:04.350207 $5,000.00 {"type": "in", "description": ""}
51 2 1 \N 2023-05-07 20:00:30 -$90.00 {"type": "out", "description": ""}
52 1 1 \N 2023-06-05 15:30:32.26733 -$8.00 {"type": "out", "description": "水果"}
53 1 4 \N 2023-06-05 15:38:53.417778 -$2.90 {"type": "out", "description": "打印作业"}
54 1 1 \N 2023-06-03 15:40:01 -$9.00 {"type": "out", "description": "奶茶"}
55 2 10 \N 2023-06-05 15:48:41.416907 -$134.00 {"type": "out", "description": "水杯"}
56 2 10 \N 2023-06-05 15:49:19.312522 -$39.90 {"type": "out", "description": "纸巾"}
57 2 10 \N 2023-06-05 15:49:55.757227 -$56.80 {"type": "out", "description": "仓鼠笼子"}
59 1 1 \N 2023-06-03 15:50:51 -$18.00 {"type": "out", "description": "晚餐"}
58 1 1 \N 2023-06-03 15:50:33 -$15.00 {"type": "out", "description": "午餐"}
60 2 9 \N 2023-06-04 15:51:39 -$4.00 {"type": "out", "description": "地铁"}
61 1 9 \N 2023-06-04 15:52:07 -$11.00 {"type": "out", "description": "打车"}
62 1 1 \N 2023-06-04 15:52:44 -$21.00 {"type": "out", "description": "午餐"}
64 1 1 \N 2023-06-04 15:53:29 -$10.00 {"type": "out", "description": "水果"}
63 1 1 \N 2023-06-04 15:53:11 -$19.00 {"type": "out", "description": "晚餐"}
65 2 9 \N 2023-04-05 15:54:54 -$58.00 {"type": "out", "description": "动车"}
66 1 9 \N 2023-04-06 15:55:25 -$58.00 {"type": "out", "description": "动车"}
67 1 7 \N 2023-06-02 15:56:39 -$100.00 {"type": "out", "description": "充水卡"}
68 1 5 \N 2023-06-01 15:57:33 $1,000.00 {"type": "in", "description": "生活费"}
69 1 1 \N 2023-06-01 15:59:30 -$57.00 {"type": "out", "description": "火锅"}
70 1 10 \N 2023-05-07 16:01:31 -$339.00 {"type": "out", "description": "鞋子"}
71 1 3 \N 2023-05-06 16:02:10 -$168.00 {"type": "out", "description": "打麻将"}
72 1 1 \N 2023-05-19 16:03:41 -$90.00 {"type": "out", "description": "吃小龙虾"}
73 1 3 \N 2023-04-13 16:04:37 -$19.00 {"type": "out", "description": "知乎会员"}
74 2 \N 1 2023-06-05 18:29:39.788525 $200.00 {"type": "transfer", "description": "Transfer"}
19 2 7 \N 2023-06-04 19:16:57 -$30.00 {"type": "out", "description": ""}
22 1 1 \N 2023-06-03 19:18:31 -$200.00 {"type": "out", "description": ""}
20 2 6 \N 2023-06-01 19:17:22 $500.00 {"type": "in", "description": ""}
\.
--
-- Name: account_a_id_seq; Type: SEQUENCE SET; Schema: public; Owner: budget
--
SELECT pg_catalog.setval('public.account_a_id_seq', 4, true);
--
-- Name: category_c_id_seq; Type: SEQUENCE SET; Schema: public; Owner: budget
--
SELECT pg_catalog.setval('public.category_c_id_seq', 10, true);
--
-- Name: transaction_t_id_seq; Type: SEQUENCE SET; Schema: public; Owner: budget
--
SELECT pg_catalog.setval('public.transaction_t_id_seq', 74, true);
--
-- Name: account account_pkey; Type: CONSTRAINT; Schema: public; Owner: budget
--
ALTER TABLE ONLY public.account
ADD CONSTRAINT account_pkey PRIMARY KEY (a_id);
--
-- Name: category category_pkey; Type: CONSTRAINT; Schema: public; Owner: budget
--
ALTER TABLE ONLY public.category
ADD CONSTRAINT category_pkey PRIMARY KEY (c_id);
--
-- Name: transaction transaction_pkey; Type: CONSTRAINT; Schema: public; Owner: budget
--
ALTER TABLE ONLY public.transaction
ADD CONSTRAINT transaction_pkey PRIMARY KEY (t_id);
--
-- Name: transaction transaction_a_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: budget
--
ALTER TABLE ONLY public.transaction
ADD CONSTRAINT transaction_a_id_fkey FOREIGN KEY (a_id) REFERENCES public.account(a_id);
--
-- Name: transaction transaction_c_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: budget
--
ALTER TABLE ONLY public.transaction
ADD CONSTRAINT transaction_c_id_fkey FOREIGN KEY (c_id) REFERENCES public.category(c_id);
--
-- Name: transaction transaction_s_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: budget
--
ALTER TABLE ONLY public.transaction
ADD CONSTRAINT transaction_s_id_fkey FOREIGN KEY (s_id) REFERENCES public.account(a_id);
--
-- PostgreSQL database dump complete
--

View file

@ -16,15 +16,23 @@ class AccountTab(TabPage):
self.accountComboBox = QComboBox() self.accountComboBox = QComboBox()
self.accountComboBox.currentIndexChanged.connect(self.onAccountIndexChanged) self.accountComboBox.currentIndexChanged.connect(self.onAccountIndexChanged)
self.accountBalanceLine = QLineEdit()
self.accountBalanceLine.setReadOnly(True)
# 默认宽度
self.accountBalanceLine.setFixedWidth(150)
self.accountModifyButton = QPushButton('Modify') self.accountModifyButton = QPushButton('Modify')
self.accountTransferButton = QPushButton('Transfer')
self.accountAddButton = QPushButton('Add') self.accountAddButton = QPushButton('Add')
self.accountModifyButton.clicked.connect(self.onAccountModifyClicked) self.accountModifyButton.clicked.connect(self.onAccountModifyClicked)
self.accountTransferButton.clicked.connect(self.onAccountTransferClicked)
self.accountAddButton.clicked.connect(self.onAccountAddClicked) self.accountAddButton.clicked.connect(self.onAccountAddClicked)
# 创建控件布局 # 创建控件布局
topLayout = QHBoxLayout() topLayout = QHBoxLayout()
topLayout.addWidget(self.accountComboBox) topLayout.addWidget(self.accountComboBox)
topLayout.addWidget(self.accountBalanceLine)
topLayout.addWidget(self.accountModifyButton) topLayout.addWidget(self.accountModifyButton)
topLayout.addWidget(self.accountTransferButton)
topLayout.addWidget(self.accountAddButton) topLayout.addWidget(self.accountAddButton)
# 创建表格 # 创建表格
@ -39,42 +47,6 @@ class AccountTab(TabPage):
self.setLayout(accountLayout) self.setLayout(accountLayout)
def onAccountDeleteClicked(self):
# 新建对话框,询问是否删除
dialog = QDialog(self)
dialog.setWindowTitle('Delete Account')
label1 = QLabel("Are you sure to delete this account?")
label2 = QLabel("This action cannot be undone.")
buttonBox = QDialogButtonBox(QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel)
buttonBox.accepted.connect(dialog.accept)
buttonBox.rejected.connect(dialog.reject)
layout = QVBoxLayout()
layout.addWidget(label1)
layout.addWidget(label2)
layout.addWidget(buttonBox)
layout.setSpacing(12)
layout.setContentsMargins(15, 15, 15, 15)
dialog.setMinimumWidth(400)
dialog.setLayout(layout)
# 若确认删除,则执行删除操作
if dialog.exec() == QDialog.DialogCode.Accepted:
index = self.accountComboBox.currentIndex()
if (index == -1):
QMessageBox.critical(self, 'Error', 'No account selected')
return
a_id = self.rows[index][0]
self.pg.execute("DELETE FROM account WHERE a_id=%s", (a_id,))
self.selected()
QMessageBox.information(self, 'Success', 'Account deleted')
# 关闭原来的对话框
self.aDialog.close()
def onAccountModifyClicked(self): def onAccountModifyClicked(self):
# 创建添加数据对话框 # 创建添加数据对话框
@ -114,16 +86,13 @@ class AccountTab(TabPage):
# descriptionLine.setText(self.transData[self.rows[0].row()][6]) # descriptionLine.setText(self.transData[self.rows[0].row()][6])
button1 = QPushButton("Confirm") button1 = QPushButton("Confirm")
button3 = QPushButton("Delete")
button2 = QPushButton("Abort") button2 = QPushButton("Abort")
buttonLayout = QHBoxLayout() buttonLayout = QHBoxLayout()
buttonLayout.addWidget(button1) buttonLayout.addWidget(button1)
buttonLayout.addWidget(button3)
buttonLayout.addWidget(button2) buttonLayout.addWidget(button2)
button1.clicked.connect(self.aDialog.accept) button1.clicked.connect(self.aDialog.accept)
button3.clicked.connect(self.onAccountDeleteClicked)
button2.clicked.connect(self.aDialog.reject) button2.clicked.connect(self.aDialog.reject)
layout = QFormLayout() layout = QFormLayout()
@ -259,11 +228,14 @@ class AccountTab(TabPage):
meta->>'description' FROM transaction \ meta->>'description' FROM transaction \
WHERE a_id = %s ORDER BY time DESC", (a_id,)) WHERE a_id = %s ORDER BY time DESC", (a_id,))
self.transData = self.pg.fetchall() self.transData = self.pg.fetchall()
self.pg.execute("SELECT a_id, meta ->> 'name' FROM account ORDER BY a_id") self.pg.execute("SELECT a_id, meta ->> 'name', translate(balance::varchar,'$','') FROM account ORDER BY a_id")
self.accountData = self.pg.fetchall() self.accountData = self.pg.fetchall()
self.pg.execute("SELECT c_id, meta ->> 'name' FROM category ORDER BY c_id") self.pg.execute("SELECT c_id, meta ->> 'name' FROM category ORDER BY c_id")
self.categoryData = self.pg.fetchall() self.categoryData = self.pg.fetchall()
# 设置余额
self.accountBalanceLine.setText(str(self.accountData[index][2]))
# 初始化表格 # 初始化表格
self.accountTable.clear() self.accountTable.clear()
self.accountTable.setRowCount(len(self.transData)) self.accountTable.setRowCount(len(self.transData))
@ -283,3 +255,73 @@ class AccountTab(TabPage):
self.accountTable.setItem(i, 2, QTableWidgetItem(str(self.transData[i][5]))) self.accountTable.setItem(i, 2, QTableWidgetItem(str(self.transData[i][5])))
self.accountTable.setItem(i, 3, QTableWidgetItem(self.transData[i][6])) self.accountTable.setItem(i, 3, QTableWidgetItem(self.transData[i][6]))
def onAccountTransferClicked(self):
# 创建添加数据对话框
self.aDialog = QDialog(self)
self.aDialog.setWindowTitle('Transfer')
srcLabel = QLabel("Source Account:")
srcComboBox = QComboBox()
for row in self.rows:
data = row[1]
srcComboBox.addItem(data)
dstLabel = QLabel("Destination Account:")
dstComboBox = QComboBox()
for row in self.rows:
data = row[1]
dstComboBox.addItem(data)
amountLabel = QLabel("Amount:")
amountLine = QLineEdit()
amountLine.setText("0.00")
# 只允许输入两位小数
amountLine.setValidator(QDoubleValidator(0.00, 999999999.99, 2))
# 按钮
button1 = QPushButton("Confirm")
button2 = QPushButton("Abort")
buttonLayout = QHBoxLayout()
buttonLayout.addWidget(button1)
buttonLayout.addWidget(button2)
button1.clicked.connect(self.aDialog.accept)
button2.clicked.connect(self.aDialog.reject)
layout = QFormLayout()
layout.addRow(srcLabel,srcComboBox)
layout.addRow(dstLabel,dstComboBox)
layout.addRow(amountLabel,amountLine)
layout.addRow(buttonLayout)
layout.setSpacing(12)
layout.setContentsMargins(15, 15, 15, 15)
self.aDialog.setMinimumWidth(400)
self.aDialog.setLayout(layout)
if self.aDialog.exec() == QDialog.DialogCode.Accepted:
try:
# 获取账户c_id
src_id = self.rows[srcComboBox.currentIndex()][0]
dst_id = self.rows[dstComboBox.currentIndex()][0]
amount = float(amountLine.text())
if (amount <= 0):
raise Exception('Amount must be greater than zero')
if (src_id == dst_id):
raise Exception('Source and destination account cannot be the same')
# 插入数据
data = {"type":"transfer", "description": "Transfer"}
data = json.dumps(data)
self.pg.execute(
"INSERT INTO transaction (time, a_id, s_id, amount, meta)\
VALUES (now(), %s, %s, %s, %s)", (dst_id, src_id, amount, data,))
# 源账户减少
self.pg.execute(
"UPDATE account SET balance = balance - (%s::NUMERIC)::MONEY WHERE a_id = %s", (amount, src_id,))
# 目标账户增加
self.pg.execute(
"UPDATE account SET balance = balance +(%s::NUMERIC)::MONEY WHERE a_id = %s", (amount, dst_id,))
# 刷新表格
self.selected()
except Exception as e:
print(e)
QMessageBox.critical(self, 'Error', str(e))

View file

@ -42,9 +42,6 @@ class CategoryTab(TabPage):
self.setLayout(categoryLayout) self.setLayout(categoryLayout)
def onCategoryDeleteClicked(self):
pass
def onCategoryModifyClicked(self): def onCategoryModifyClicked(self):
# 创建添加数据对话框 # 创建添加数据对话框
self.cDialog = QDialog(self) self.cDialog = QDialog(self)
@ -83,15 +80,12 @@ class CategoryTab(TabPage):
line3.setText(data[0][2]) line3.setText(data[0][2])
button1 = QPushButton("Confirm") button1 = QPushButton("Confirm")
button3 = QPushButton("Delete")
button2 = QPushButton("Abort") button2 = QPushButton("Abort")
buttonLayout = QHBoxLayout() buttonLayout = QHBoxLayout()
buttonLayout.addWidget(button1) buttonLayout.addWidget(button1)
buttonLayout.addWidget(button3)
buttonLayout.addWidget(button2) buttonLayout.addWidget(button2)
button1.clicked.connect(self.cDialog.accept) button1.clicked.connect(self.cDialog.accept)
button3.clicked.connect(self.onCategoryDeleteClicked)
button2.clicked.connect(self.cDialog.reject) button2.clicked.connect(self.cDialog.reject)
layout = QFormLayout() layout = QFormLayout()

View file

@ -15,12 +15,15 @@ class TransTab(TabPage):
# 创建顶部控件 # 创建顶部控件
transAddButton = QPushButton('Add') transAddButton = QPushButton('Add')
transAddButton.clicked.connect(self.onTransAddClicked) transAddButton.clicked.connect(self.onTransAddClicked)
self.transSearchButton = QPushButton('Search')
self.transSearchButton.clicked.connect(self.onTransSearchClicked)
transModifyButton = QPushButton('Modify') transModifyButton = QPushButton('Modify')
transModifyButton.clicked.connect(self.onTransModifyClicked) transModifyButton.clicked.connect(self.onTransModifyClicked)
# 创建控件布局 # 创建控件布局
topLayout = QHBoxLayout() topLayout = QHBoxLayout()
topLayout.addWidget(transAddButton) topLayout.addWidget(transAddButton)
topLayout.addWidget(self.transSearchButton)
topLayout.addWidget(transModifyButton) topLayout.addWidget(transModifyButton)
self.transTable = QTableWidget() self.transTable = QTableWidget()
@ -238,7 +241,8 @@ class TransTab(TabPage):
self.dialogLayout.itemAt(5).widget().addItem(self.accountData[i][1]) self.dialogLayout.itemAt(5).widget().addItem(self.accountData[i][1])
def selected(self): def selected(self):
self.pg.execute("SELECT t_id, a_id, c_id, s_id, time, translate(amount::varchar,'$',''), meta->>'description' FROM transaction ORDER BY time DESC") self.pg.execute("SELECT t_id, a_id, c_id, s_id, time, translate(amount::varchar,'$',''), meta->>'description' \
FROM transaction ORDER BY time DESC")
self.transData = self.pg.fetchall() self.transData = self.pg.fetchall()
self.pg.execute("SELECT a_id, meta ->> 'name' FROM account ORDER BY a_id") self.pg.execute("SELECT a_id, meta ->> 'name' FROM account ORDER BY a_id")
self.accountData = self.pg.fetchall() self.accountData = self.pg.fetchall()
@ -264,3 +268,66 @@ class TransTab(TabPage):
self.transTable.setItem(i, 2, QTableWidgetItem(self.categoryData[self.transData[i][2]-1][1])) self.transTable.setItem(i, 2, QTableWidgetItem(self.categoryData[self.transData[i][2]-1][1]))
self.transTable.setItem(i, 3, QTableWidgetItem(str(self.transData[i][5]))) self.transTable.setItem(i, 3, QTableWidgetItem(str(self.transData[i][5])))
self.transTable.setItem(i, 4, QTableWidgetItem(self.transData[i][6])) self.transTable.setItem(i, 4, QTableWidgetItem(self.transData[i][6]))
def onTransSearchClicked(self):
# 若已经搜索,则重置
if self.transSearchButton.text() == "Reset":
self.selected()
self.transSearchButton.setText("Search")
return
self.tDialog = QDialog(self)
self.tDialog.setWindowTitle('Search Transaction')
discLabel=QLabel("Discription:")
discLine=QLineEdit()
discLine.setText("")
cButton = QPushButton("Confirm")
aButton = QPushButton("Abort")
buttonLayout = QHBoxLayout()
buttonLayout.addWidget(cButton)
buttonLayout.addWidget(aButton)
cButton.clicked.connect(self.tDialog.accept)
aButton.clicked.connect(self.tDialog.reject)
dialogLayout = QFormLayout()
dialogLayout.addRow(discLabel, discLine)
dialogLayout.addRow(buttonLayout)
dialogLayout.setSpacing(12)
dialogLayout.setContentsMargins(15, 15, 15, 15)
self.tDialog.setMinimumWidth(400)
self.tDialog.setLayout(dialogLayout)
if self.tDialog.exec() == QDialog.DialogCode.Accepted:
discription = discLine.text()
self.pg.execute("SELECT t_id, a_id, c_id, s_id, time, translate(amount::varchar,'$',''), meta->>'description'\
FROM transaction WHERE meta->>'description' LIKE %s ORDER BY time DESC",('%'+discription+'%',))
self.transData = self.pg.fetchall()
self.pg.execute("SELECT a_id, meta ->> 'name' FROM account ORDER BY a_id")
self.accountData = self.pg.fetchall()
self.pg.execute("SELECT c_id, meta ->> 'name' FROM category ORDER BY c_id")
self.categoryData = self.pg.fetchall()
# 设置表格
self.transTable.clear()
self.transTable.setRowCount(len(self.transData))
self.transTable.setColumnCount(5) # 时间 账户 类别 金额 描述
self.transTable.setHorizontalHeaderLabels(['Time', 'Account', 'Category', 'Amount', 'Description'])
# 设置自动调整列宽
self.transTable.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeMode.Stretch)
# 填充表格
for i in range(len(self.transData)):
self.transTable.setItem(i, 0, QTableWidgetItem(self.transData[i][4].strftime("%Y-%m-%d %H:%M:%S")))
self.transTable.setItem(i, 1, QTableWidgetItem(self.accountData[self.transData[i][1]-1][1]))
if self.transData[i][2] is None:
self.transTable.setItem(i, 2, QTableWidgetItem("None"))
else:
self.transTable.setItem(i, 2, QTableWidgetItem(self.categoryData[self.transData[i][2]-1][1]))
self.transTable.setItem(i, 3, QTableWidgetItem(str(self.transData[i][5])))
self.transTable.setItem(i, 4, QTableWidgetItem(self.transData[i][6]))
# 设置按钮文本为重置
self.transSearchButton.setText("Reset")