Description
Currently, Oinkoin forces the sign of a record's value based on its category type:
- Expense category → value is always stored as negative (e.g., -50€)
- Income category → value is always stored as positive (e.g., +100€)
This prevents users from recording refunds or paybacks in the same category where the original transaction occurred. For example:
- A 10€ refund on groceries cannot be entered in the "Groceries" expense category (the app turns it into -10€, making expenses look even larger)
- A 100€ payback of income (employer mistake) cannot be entered as negative income
The workaround (creating a separate "Refunds" income category) inflates statistics — showing more expenses AND more income than reality.
Requested Behavior (inspired by "Mein Haushaltsbuch")
Users should be able to enter negative amounts in any category:
- -10€ in an expense category → reduces that category's expense total (net expense of -40€ instead of separate -50€ expense + 10€ refund in another category)
- -100€ in an income category → reduces income totals
- Statistics show realistic net totals per category
Technical Analysis
No database changes needed
The records table stores value REAL with no CHECK constraints (lib/services/database/sqlite-migration-service.dart:37). The sign of the value is not enforced at the DB level.
What needs to change
-
lib/records/edit-record-page.dart (line 967-989):
- Change
allowNegative: false → allowNegative: true on the AmountInputField
- Remove the forced sign assignment in
changeRecordValue() — stop calling abs() and re-negating based on category type. Let the user's entered sign be stored as-is.
-
lib/components/amount_input_field.dart — Already supports allowNegative: true (line 24), no changes needed.
-
lib/helpers/records-utility-functions.dart — The parsers tryParseCurrencyString and tryParseSignedCurrencyString already exist (lines 213-220).
Statistics impact
Most aggregation code groups records by CategoryType and sums values directly, which handles refunds correctly:
records_per_day.dart — sums all values for a given CategoryType → refunds naturally reduce totals ✓
statistics-page.dart — filters by CategoryType → refunds in expense categories show up in the Expenses tab ✓
balance-chart-models.dart — uses value.abs() → may need review for records with "wrong" signs
Some edge cases in lib/models/records-per-category.dart — uses value < 0 / value > 0 to split income/expense within a category. A positive refund in an expense category would show as "income" in the per-category breakdown rather than reducing expenses. This may need updating.
Files Likely Affected
lib/records/edit-record-page.dart — Remove sign enforcement
lib/models/records-per-category.dart — Review sign-based splitting
lib/statistics/balance-chart-models.dart — Review value.abs() usage
lib/records/controllers/tab_records_controller.dart — Review applyTransferAwareWalletFilter for edge cases
Description
Currently, Oinkoin forces the sign of a record's value based on its category type:
This prevents users from recording refunds or paybacks in the same category where the original transaction occurred. For example:
The workaround (creating a separate "Refunds" income category) inflates statistics — showing more expenses AND more income than reality.
Requested Behavior (inspired by "Mein Haushaltsbuch")
Users should be able to enter negative amounts in any category:
Technical Analysis
No database changes needed
The
recordstable storesvalue REALwith no CHECK constraints (lib/services/database/sqlite-migration-service.dart:37). The sign of the value is not enforced at the DB level.What needs to change
lib/records/edit-record-page.dart(line 967-989):allowNegative: false→allowNegative: trueon theAmountInputFieldchangeRecordValue()— stop callingabs()and re-negating based on category type. Let the user's entered sign be stored as-is.lib/components/amount_input_field.dart— Already supportsallowNegative: true(line 24), no changes needed.lib/helpers/records-utility-functions.dart— The parserstryParseCurrencyStringandtryParseSignedCurrencyStringalready exist (lines 213-220).Statistics impact
Most aggregation code groups records by
CategoryTypeand sums values directly, which handles refunds correctly:records_per_day.dart— sums all values for a givenCategoryType→ refunds naturally reduce totals ✓statistics-page.dart— filters byCategoryType→ refunds in expense categories show up in the Expenses tab ✓balance-chart-models.dart— usesvalue.abs()→ may need review for records with "wrong" signsSome edge cases in
lib/models/records-per-category.dart— usesvalue < 0/value > 0to split income/expense within a category. A positive refund in an expense category would show as "income" in the per-category breakdown rather than reducing expenses. This may need updating.Files Likely Affected
lib/records/edit-record-page.dart— Remove sign enforcementlib/models/records-per-category.dart— Review sign-based splittinglib/statistics/balance-chart-models.dart— Reviewvalue.abs()usagelib/records/controllers/tab_records_controller.dart— ReviewapplyTransferAwareWalletFilterfor edge cases