Skip to content

Commit 03de4aa

Browse files
author
codewithcj001
committed
Updated Transaction rule to include AccountName and Exact/Contains pattern matching.
1 parent d8a9455 commit 03de4aa

File tree

7 files changed

+459
-22
lines changed

7 files changed

+459
-22
lines changed
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
-- SparkyBudget_Upgrade_v0.20.sql
2+
3+
-- Add AccountName column to D_Category_Rule
4+
ALTER TABLE D_Category_Rule ADD COLUMN AccountName TEXT;
5+
6+
-- Add TransactionAmount column to D_Category_Rule
7+
ALTER TABLE D_Category_Rule ADD COLUMN TransactionAmount REAL;
8+
9+
-- Add TransactionDate column to D_Category_Rule
10+
ALTER TABLE D_Category_Rule ADD COLUMN TransactionDate DATE;
11+
12+
-- Set default values for existing rows
13+
UPDATE D_Category_Rule SET AccountName = 'ALL' WHERE AccountName IS NULL;
14+
-- Rule_Pattern already exists and defaults to 'Contains' for existing rules, so no change needed here.
15+
16+
17+
DROP TRIGGER IF EXISTS tr_insert_stg_transaction;
18+
19+
CREATE TRIGGER tr_insert_stg_transaction
20+
AFTER INSERT ON stg_transaction
21+
FOR EACH ROW
22+
BEGIN
23+
-- Insert or replace record in f_transaction
24+
INSERT OR REPLACE INTO f_transaction (
25+
TransactionKey,
26+
AccountID,
27+
AccountName,
28+
TransactionID,
29+
TransactionPosted,
30+
TransactionAmount,
31+
TransactionDescription,
32+
TransactionPayee,
33+
TransactionMemo,
34+
TransactionPending,
35+
SubCategory
36+
) VALUES (
37+
(SELECT TransactionKey FROM f_transaction WHERE TransactionID = NEW.TransactionID),
38+
NEW.AccountID,
39+
NEW.AccountName,
40+
NEW.TransactionID,
41+
NEW.TransactionPosted,
42+
NEW.TransactionAmount,
43+
NEW.TransactionDescription,
44+
NEW.TransactionPayee,
45+
NEW.TransactionMemo,
46+
NEW.TransactionPending,
47+
(SELECT SubCategory FROM f_transaction WHERE TransactionID = NEW.TransactionID)
48+
);
49+
50+
-- Remove the specific record from stg_transaction after insert
51+
DELETE FROM stg_transaction WHERE TransactionID = NEW.TransactionID;
52+
53+
UPDATE F_Transaction
54+
SET SubCategory = (
55+
SELECT Default_SubCategory FROM (
56+
SELECT Default_SubCategory, D_Category_Rule.AccountName
57+
FROM D_Category_Rule
58+
WHERE
59+
(LOWER(NEW.TransactionPayee) LIKE '%' || LOWER(D_Category_Rule.Match_Word) || '%'
60+
OR LOWER(D_Category_Rule.Match_Word) LIKE '%' || LOWER(NEW.TransactionPayee) || '%')
61+
AND D_Category_Rule.Rule_Category = 'Payee'
62+
AND D_Category_Rule.Rule_Pattern = 'Contains'
63+
AND D_Category_Rule.AccountName = NEW.AccountName
64+
UNION ALL
65+
SELECT Default_SubCategory, D_Category_Rule.AccountName
66+
FROM D_Category_Rule
67+
WHERE
68+
(LOWER(NEW.TransactionPayee) LIKE '%' || LOWER(D_Category_Rule.Match_Word) || '%'
69+
OR LOWER(D_Category_Rule.Match_Word) LIKE '%' || LOWER(NEW.TransactionPayee) || '%')
70+
AND D_Category_Rule.Rule_Category = 'Payee'
71+
AND D_Category_Rule.Rule_Pattern = 'Contains'
72+
AND D_Category_Rule.AccountName = 'ALL'
73+
)
74+
ORDER BY
75+
CASE WHEN AccountName = NEW.AccountName THEN 0 ELSE 1 END,
76+
CASE WHEN Rule_Pattern = 'Exact' THEN 0 ELSE 1 END
77+
LIMIT 1
78+
)
79+
WHERE F_Transaction.TransactionID = NEW.TransactionID
80+
AND SubCategory IS NULL
81+
AND EXISTS (
82+
SELECT 1
83+
FROM D_Category_Rule
84+
WHERE
85+
(LOWER(NEW.TransactionPayee) LIKE '%' || LOWER(D_Category_Rule.Match_Word) || '%'
86+
OR LOWER(D_Category_Rule.Match_Word) LIKE '%' || LOWER(NEW.TransactionPayee) || '%')
87+
AND D_Category_Rule.Rule_Category = 'Payee'
88+
AND (D_Category_Rule.Rule_Pattern = 'Contains' OR D_Category_Rule.Rule_Pattern = 'Exact')
89+
AND (D_Category_Rule.AccountName = NEW.AccountName OR D_Category_Rule.AccountName = 'ALL')
90+
);
91+
92+
END
93+
;
94+
95+
-- Update DB_VERSION
96+
97+
-- Update DB_VERSION
98+
UPDATE D_DB SET DB_VERSION = "v0.20";

SparkyBudget/py_routes/manage_categories.py

Lines changed: 47 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -123,8 +123,9 @@ def add_subcategory_rule():
123123
try:
124124
# Get form data from the request
125125
default_subcategory = request.form['subcategoryDropDown']
126+
account_name = request.form.get('accountNameDropDown', 'ALL') # Get AccountName, default to 'ALL'
126127
rule_category = 'Payee'
127-
rule_pattern = 'Contains'
128+
rule_pattern = request.form.get('rulePatternDropDown', 'Contains') # Get Rule_Pattern, default to 'Contains'
128129
match_word = request.form['matchword']
129130

130131
# Connect to the SQLite database
@@ -133,12 +134,12 @@ def add_subcategory_rule():
133134

134135
# SQL query to insert the new rule
135136
insert_query = """
136-
INSERT INTO D_Category_Rule (Default_SubCategory, Rule_Category, Rule_Pattern, Match_Word)
137-
VALUES (?, ?, ?, ?)
137+
INSERT INTO D_Category_Rule (Default_SubCategory, AccountName, Rule_Category, Rule_Pattern, Match_Word)
138+
VALUES (?, ?, ?, ?, ?)
138139
"""
139140

140141
# Execute the query with the form data
141-
cursor.execute(insert_query, (default_subcategory, rule_category, rule_pattern, match_word))
142+
cursor.execute(insert_query, (default_subcategory, account_name, rule_category, rule_pattern, match_word))
142143
conn.commit() # Commit the transaction
143144
conn.close()
144145

@@ -250,13 +251,14 @@ def subcategory_rule_data():
250251
SELECT
251252
RuleKey,
252253
Default_SubCategory,
254+
AccountName,
253255
Rule_Category,
254256
Rule_Pattern,
255257
Match_Word
256258
FROM
257-
D_Category_Rule
259+
D_Category_Rule
258260
ORDER BY
259-
2,3,4,5 ASC
261+
AccountName ASC, Default_SubCategory ASC, Rule_Category ASC, Rule_Pattern ASC, Match_Word ASC
260262
"""
261263
cursor.execute(subcategory_rule_query)
262264
subcategory_rule_data = cursor.fetchall()
@@ -267,9 +269,10 @@ def subcategory_rule_data():
267269
{
268270
"RuleKey": row[0],
269271
"Default_SubCategory": row[1],
270-
"Rule_Category": row[2],
271-
"Rule_Pattern": row[3],
272-
"Match_Word": row[4],
272+
"AccountName": row[2],
273+
"Rule_Category": row[3],
274+
"Rule_Pattern": row[4],
275+
"Match_Word": row[5],
273276
}
274277
for row in subcategory_rule_data
275278
]
@@ -288,15 +291,17 @@ def update_rule():
288291
try:
289292
RuleKey = request.form["RuleKey"]
290293
Match_Word = request.form["Match_Word"]
294+
AccountName = request.form.get("AccountName", "ALL")
295+
Rule_Pattern = request.form.get("Rule_Pattern", "Contains")
291296
conn = sqlite3.connect(current_app.config['DATABASE_PATH'])
292297
cursor = conn.cursor()
293298

294299
update_rule_query = """
295300
UPDATE D_Category_Rule
296-
SET Match_Word = ?
301+
SET Match_Word = ?, AccountName = ?, Rule_Pattern = ?
297302
WHERE RuleKey = ?
298303
"""
299-
update_rule_data = (Match_Word, RuleKey)
304+
update_rule_data = (Match_Word, AccountName, Rule_Pattern, RuleKey)
300305

301306
cursor.execute(update_rule_query, update_rule_data)
302307
conn.commit()
@@ -333,6 +338,36 @@ def delete_rule(RuleKey):
333338
return jsonify({"error": str(e)}), 500
334339

335340

341+
@manage_categories_bp.route('/getAccountNames')
342+
@login_required
343+
def account_names_data():
344+
try:
345+
conn = sqlite3.connect(current_app.config['DATABASE_PATH'])
346+
cursor = conn.cursor()
347+
348+
account_names_query = """
349+
SELECT DISTINCT
350+
COALESCE(DisplayAccountName, AccountName)
351+
FROM
352+
F_Balance
353+
ORDER BY
354+
1 ASC
355+
"""
356+
cursor.execute(account_names_query)
357+
account_names = cursor.fetchall()
358+
conn.close()
359+
360+
account_names_list = [{"AccountName": row[0]} for row in account_names]
361+
account_names_list.insert(0, {"AccountName": "ALL"}) # Add 'ALL' option
362+
363+
return jsonify(account_names_list)
364+
except Exception as e:
365+
logger.error(f"An error occurred: {str(e)}", exc_info=True)
366+
return jsonify({"error": str(e)}), 500
367+
368+
369+
370+
336371
@manage_categories_bp.route('/getAccountTypes')
337372
@login_required
338373
def account_type_data():
@@ -356,7 +391,7 @@ def account_type_data():
356391
conn.close()
357392

358393
# Format the fetched data for JSON response
359-
account_types_list = [
394+
account_types_list = [
360395
{"AccountType": row[0], "HideFromBudget": row[1], "SortOrder": row[2]} for row in account_types
361396
] # Assuming 'AccountType', 'HideFromBudget', 'SortOrder' are column names in your table
362397

SparkyBudget/static/css/category.css

Lines changed: 91 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,33 @@
137137
.AddSubCategoryContainer input[type="text"],
138138
.AddSubCategoryRuleContainer select,
139139
.AddSubCategoryRuleContainer input[type="text"] {
140-
flex-grow: 1; /* Allow inputs and selects to take available space */
140+
/* Removed flex-grow: 1 to prevent overriding explicit width settings */
141+
background-color: #3c444f !important; /* Dark background for dropdowns and input */
142+
color: #eee !important; /* Light text color */
143+
border: 1px solid #555 !important; /* Subtle border */
144+
border-radius: 4px; /* Rounded corners */
145+
padding: 6px 10px !important; /* Adjust padding */
146+
-moz-appearance: none; /* Remove default Firefox styles */
147+
appearance: none; /* Remove default system styles */
148+
min-width: 150px; /* Ensure a minimum width for dropdowns */
149+
}
150+
151+
.AddSubCategoryRuleContainer input[type="text"]::placeholder {
152+
color: #bbb !important; /* Lighter placeholder text color */
153+
opacity: 1; /* Ensure placeholder is visible */
154+
}
155+
156+
.AddSubCategoryRuleContainer select:focus,
157+
.AddSubCategoryRuleContainer input[type="text"]:focus {
158+
border-color: #3399cc !important; /* Highlight border on focus */
159+
outline: none !important; /* Remove default outline */
160+
box-shadow: 0 0 5px rgba(51, 153, 204, 0.5); /* Subtle glow on focus */
161+
}
162+
163+
/* Style for dropdown options */
164+
.AddSubCategoryRuleContainer select option {
165+
background-color: #3c444f !important; /* Dark background for options */
166+
color: #eee !important; /* Light text color for options */
141167
}
142168

143169
/* --- Container and Table Widths --- */
@@ -236,6 +262,70 @@ border-radius: 12px;
236262
background-color: transparent !important; /* Prevent any white background */
237263
}
238264

265+
/* Select2 adjustments for dark theme and width */
266+
.select2-container--default .select2-selection--single {
267+
background-color: #3c444f !important; /* Dark background for Select2 input */
268+
color: #eee !important; /* Light text color for Select2 input */
269+
border: 1px solid #555 !important; /* Subtle border */
270+
border-radius: 4px; /* Rounded corners */
271+
height: 38px; /* Adjust height to match other inputs */
272+
display: flex;
273+
align-items: center;
274+
}
275+
276+
.select2-container--default .select2-selection--single .select2-selection__rendered {
277+
color: #eee !important; /* Light text color for selected item */
278+
line-height: 38px; /* Center text vertically */
279+
padding-left: 10px;
280+
}
281+
282+
.select2-container--default .select2-selection--single .select2-selection__arrow {
283+
height: 36px; /* Adjust arrow height */
284+
right: 5px;
285+
}
286+
287+
.select2-container--default .select2-selection--single .select2-selection__arrow b {
288+
border-color: #eee transparent transparent transparent !important; /* Light arrow color */
289+
}
290+
291+
.select2-container--default.select2-container--open .select2-selection--single .select2-selection__arrow b {
292+
border-color: transparent transparent #eee transparent !important; /* Light arrow color when open */
293+
}
294+
295+
.select2-dropdown {
296+
background-color: #3c444f !important; /* Dark background for dropdown menu */
297+
border: 1px solid #555 !important; /* Subtle border */
298+
border-radius: 4px; /* Rounded corners */
299+
}
300+
301+
.select2-container--default .select2-results__option {
302+
color: #eee !important; /* Light text color for options */
303+
padding: 8px 10px;
304+
}
305+
306+
.select2-container--default .select2-results__option--highlighted.select2-results__option--selectable {
307+
background-color: #555 !important; /* Highlight background for selected option */
308+
color: #fff !important; /* White text for highlighted option */
309+
}
310+
311+
/* Specific width adjustments for Select2 dropdowns in the Transaction Rules section */
312+
#subcategoryDropDown + .select2-container,
313+
#accountNameDropDown + .select2-container,
314+
#rulePatternDropDown + .select2-container {
315+
width: 200px !important;
316+
}
317+
318+
.select2-container--default .select2-search--dropdown .select2-search__field {
319+
background-color: #2E3440 !important; /* Dark background for search input in dropdown */
320+
color: #eee !important; /* Light text color for search input */
321+
border: 1px solid #555 !important;
322+
}
323+
324+
/* Ensure width is applied correctly by Select2 */
325+
.select2-container {
326+
width: 200px !important; /* Apply the desired width */
327+
}
328+
239329

240330
@media (max-width: 768px) {
241331
.mobile-header-logo .nav-icon {

0 commit comments

Comments
 (0)