diff --git a/scripts/create_table.sql b/scripts/create_table.sql index 62981f1..8f887fb 100644 --- a/scripts/create_table.sql +++ b/scripts/create_table.sql @@ -1,11 +1,10 @@ -- 账户表 CREATE TABLE account ( a_id SERIAL PRIMARY KEY, -- 账户号 - balance MONEY, -- 余额 - meta JSONB -- 元数据 + balance MONEY, -- 余额 + meta JSONB -- 元数据 ); --- {"name":"name", --- time:"2022-02-02", +-- {"name":"name", -- “budget": [ -- {"time":"2022-02","value":20.32}, -- {"time":"2022-01","value":30.32} @@ -14,8 +13,8 @@ CREATE TABLE account ( -- 类别表 CREATE TABLE category ( - c_id SERIAL PRIMARY KEY, -- 类别号 - meta JSONB -- 元数据 + c_id SERIAL PRIMARY KEY, -- 类别号 + meta JSONB -- 元数据 ); -- {"name":"name", -- "type":"in"/"out", @@ -27,19 +26,18 @@ CREATE TABLE category ( -- 流水表 CREATE TABLE transaction ( - t_id SERIAL PRIMARY KEY, -- 流水号 + t_id SERIAL PRIMARY KEY, -- 流水号 a_id INTEGER REFERENCES account(a_id) NOT NULL, -- 关联账户号 - c_id INTEGER REFERENCES category(c_id), -- 关联类别号 - s_id INTEGER REFERENCES category(c_id), -- 源账户号 + c_id INTEGER REFERENCES category(c_id), -- 关联类别号 + s_id INTEGER REFERENCES account(a_id), -- 源账户号 time TIMESTAMP, amount MONEY, meta JSONB -- 元数据 ); --- {"discription":"something",(可选) --- "type":"in"/"out"/"transfer"/"init"/"modify", - --- "reimburse":{ (报销) --- "finish": true, +-- {"discription":"something", +-- "type":"in"/"out"/"transfer"/"init", +-- "reimburse":{ +-- "finish": true/false, -- "ref":t_id -- } -- "":"something"} diff --git a/scripts/data.sql b/scripts/data.sql new file mode 100644 index 0000000..211775e --- /dev/null +++ b/scripts/data.sql @@ -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 +-- + diff --git a/src/budget/_account.py b/src/budget/_account.py index 57a5d8f..b119dde 100644 --- a/src/budget/_account.py +++ b/src/budget/_account.py @@ -15,16 +15,24 @@ class AccountTab(TabPage): # 创建顶部控件 self.accountComboBox = QComboBox() self.accountComboBox.currentIndexChanged.connect(self.onAccountIndexChanged) - + + self.accountBalanceLine = QLineEdit() + self.accountBalanceLine.setReadOnly(True) + # 默认宽度 + self.accountBalanceLine.setFixedWidth(150) self.accountModifyButton = QPushButton('Modify') + self.accountTransferButton = QPushButton('Transfer') self.accountAddButton = QPushButton('Add') self.accountModifyButton.clicked.connect(self.onAccountModifyClicked) + self.accountTransferButton.clicked.connect(self.onAccountTransferClicked) self.accountAddButton.clicked.connect(self.onAccountAddClicked) # 创建控件布局 topLayout = QHBoxLayout() topLayout.addWidget(self.accountComboBox) + topLayout.addWidget(self.accountBalanceLine) topLayout.addWidget(self.accountModifyButton) + topLayout.addWidget(self.accountTransferButton) topLayout.addWidget(self.accountAddButton) # 创建表格 @@ -39,42 +47,6 @@ class AccountTab(TabPage): 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): # 创建添加数据对话框 @@ -114,16 +86,13 @@ class AccountTab(TabPage): # descriptionLine.setText(self.transData[self.rows[0].row()][6]) button1 = QPushButton("Confirm") - button3 = QPushButton("Delete") button2 = QPushButton("Abort") buttonLayout = QHBoxLayout() buttonLayout.addWidget(button1) - buttonLayout.addWidget(button3) buttonLayout.addWidget(button2) button1.clicked.connect(self.aDialog.accept) - button3.clicked.connect(self.onAccountDeleteClicked) button2.clicked.connect(self.aDialog.reject) layout = QFormLayout() @@ -259,11 +228,14 @@ class AccountTab(TabPage): meta->>'description' FROM transaction \ WHERE a_id = %s ORDER BY time DESC", (a_id,)) 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.pg.execute("SELECT c_id, meta ->> 'name' FROM category ORDER BY c_id") self.categoryData = self.pg.fetchall() + # 设置余额 + self.accountBalanceLine.setText(str(self.accountData[index][2])) + # 初始化表格 self.accountTable.clear() 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, 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)) diff --git a/src/budget/_category.py b/src/budget/_category.py index 0eb4818..19c48c6 100644 --- a/src/budget/_category.py +++ b/src/budget/_category.py @@ -42,9 +42,6 @@ class CategoryTab(TabPage): self.setLayout(categoryLayout) - def onCategoryDeleteClicked(self): - pass - def onCategoryModifyClicked(self): # 创建添加数据对话框 self.cDialog = QDialog(self) @@ -83,15 +80,12 @@ class CategoryTab(TabPage): line3.setText(data[0][2]) button1 = QPushButton("Confirm") - button3 = QPushButton("Delete") button2 = QPushButton("Abort") buttonLayout = QHBoxLayout() buttonLayout.addWidget(button1) - buttonLayout.addWidget(button3) buttonLayout.addWidget(button2) button1.clicked.connect(self.cDialog.accept) - button3.clicked.connect(self.onCategoryDeleteClicked) button2.clicked.connect(self.cDialog.reject) layout = QFormLayout() diff --git a/src/budget/_trans.py b/src/budget/_trans.py index 67fdeb6..1c16462 100644 --- a/src/budget/_trans.py +++ b/src/budget/_trans.py @@ -15,12 +15,15 @@ class TransTab(TabPage): # 创建顶部控件 transAddButton = QPushButton('Add') transAddButton.clicked.connect(self.onTransAddClicked) + self.transSearchButton = QPushButton('Search') + self.transSearchButton.clicked.connect(self.onTransSearchClicked) transModifyButton = QPushButton('Modify') transModifyButton.clicked.connect(self.onTransModifyClicked) # 创建控件布局 topLayout = QHBoxLayout() topLayout.addWidget(transAddButton) + topLayout.addWidget(self.transSearchButton) topLayout.addWidget(transModifyButton) self.transTable = QTableWidget() @@ -238,7 +241,8 @@ class TransTab(TabPage): self.dialogLayout.itemAt(5).widget().addItem(self.accountData[i][1]) 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.pg.execute("SELECT a_id, meta ->> 'name' FROM account ORDER BY a_id") 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, 3, QTableWidgetItem(str(self.transData[i][5]))) 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")