@@ -5,15 +5,18 @@ import { Currency } from "../database/entities/Currency";
55import { User } from "../database/entities/User" ;
66import { Group } from "../database/entities/Group" ;
77import { TransactionNotificationService } from "./TransactionNotificationService" ;
8+ import { GroupService } from "./GroupService" ;
89import crypto from "crypto" ;
910
1011export class LedgerService {
1112 ledgerRepository : Repository < Ledger > ;
1213 currencyRepository : Repository < Currency > ;
14+ groupService : GroupService ;
1315
1416 constructor ( ) {
1517 this . ledgerRepository = AppDataSource . getRepository ( Ledger ) ;
1618 this . currencyRepository = AppDataSource . getRepository ( Currency ) ;
19+ this . groupService = new GroupService ( ) ;
1720 }
1821
1922 private computeHash ( payload : any ) : string {
@@ -28,6 +31,23 @@ export class LedgerService {
2831 return prev ?. hash ?? null ;
2932 }
3033
34+ /**
35+ * Validates whether a debit of `amount` from `currentBalance` is allowed
36+ * given the currency's negative-balance settings.
37+ */
38+ private validateNegativeAllowance ( currency : Currency , currentBalance : number , amount : number ) : void {
39+ if ( ! currency . allowNegative && currentBalance < amount ) {
40+ throw new Error ( "Insufficient balance. This currency does not allow negative balances." ) ;
41+ }
42+
43+ if ( currency . allowNegative && currency . maxNegativeBalance !== null && currency . maxNegativeBalance !== undefined ) {
44+ const newBalance = currentBalance - amount ;
45+ if ( newBalance < Number ( currency . maxNegativeBalance ) ) {
46+ throw new Error ( `Insufficient balance. This currency allows negative balances down to ${ currency . maxNegativeBalance } .` ) ;
47+ }
48+ }
49+ }
50+
3151 async getAccountBalance ( currencyId : string , accountId : string , accountType : AccountType ) : Promise < number > {
3252 const latestEntry = await this . ledgerRepository . findOne ( {
3353 where : {
@@ -132,16 +152,23 @@ export class LedgerService {
132152
133153 const currentBalance = await this . getAccountBalance ( currencyId , fromAccountId , fromAccountType ) ;
134154
135- // Validate debit bounds
136- if ( ! currency . allowNegative && currentBalance < amount ) {
137- throw new Error ( "Insufficient balance. This currency does not allow negative balances." ) ;
138- }
139-
140- if ( currency . allowNegative && currency . maxNegativeBalance !== null && currency . maxNegativeBalance !== undefined ) {
141- const newBalance = currentBalance - amount ;
142- if ( newBalance < Number ( currency . maxNegativeBalance ) ) {
143- throw new Error ( `Insufficient balance. This currency allows negative balances down to ${ currency . maxNegativeBalance } .` ) ;
155+ // Validate debit bounds.
156+ // When allowNegativeGroupOnly is true, only group members may overdraft;
157+ // non-members are limited to their current balance. Note: the invariant
158+ // allowNegativeGroupOnly ⇒ allowNegative is enforced at creation time
159+ // in CurrencyService.createCurrency.
160+ if ( currency . allowNegativeGroupOnly && fromAccountType === AccountType . USER ) {
161+ const isMember = await this . groupService . isUserInGroup ( currency . groupId , fromAccountId ) ;
162+
163+ if ( ! isMember ) {
164+ if ( currentBalance < amount ) {
165+ throw new Error ( "Insufficient balance. Only group members can have negative balances for this currency." ) ;
166+ }
167+ } else {
168+ this . validateNegativeAllowance ( currency , currentBalance , amount ) ;
144169 }
170+ } else {
171+ this . validateNegativeAllowance ( currency , currentBalance , amount ) ;
145172 }
146173
147174 // Create debit entry (from sender's account)
0 commit comments