diff --git a/accounting/accounting_config.py b/accounting/accounting_config.py index ca49be5..610e3d3 100644 --- a/accounting/accounting_config.py +++ b/accounting/accounting_config.py @@ -1,5 +1,6 @@ import asyncio import re +from operator import itemgetter from .const import * from .accountingnode import get_parent_orgid from .excep import * @@ -37,10 +38,12 @@ class Accounting: get_account(leg,accounting_orgid) 获得记账账号(通过科目,机构类型,账务机构确定一个唯一的账号) """ def __init__(self, caller): + if isinstance(caller, list): + self.callers = caller + caller = self.callers[0] self.caller = caller self.curdate = caller.curdate self.realtimesettled = False - self.curdte = caller.curdate self.timestamp = caller.timestamp self.billid = caller.billid self.action = caller.action @@ -53,15 +56,42 @@ class Accounting: self.reseller_salemode = None self.variable = caller.variable + async def setup_all_accounting_legs(self): + self.accounting_legs = [] + for caller in self.callers: + self.caller = caller + self.curdate = caller.curdate + self.realtimesettled = False + self.timestamp = caller.timestamp + self.billid = caller.billid + self.action = caller.action + self.summary = f'{self.caller.orderid}:{self.caller.billid}' + self.providerid = caller.providerid + self.productid = caller.productid + self.resellerid = caller.resellerid + self.customerid = caller.customerid + self.own_salemode = None + self.reseller_salemode = None + self.variable = caller.variable + self.setup_accounting_legs() + legs = sorted( + self.accounting_legs, + key=lambda x: ( + x.get('accounting_orgid','0'), + x.get('accid', '0'), + 0 if x.get('acc_dir', '0') == x.get('balance_at', '0') else 1 + ) + ) + self.accounting_legs = legs + async def setup_accounting_legs(self): - global accounting_config action = self.action.split('_')[0] - await get_accounting_config(self.sor) - self.accounting_legs = [r.copy() for r in accounting_config + acfg = get_accounting_config(self.sor) + legs = [r.copy() for r in acfg if r.action == action ] - debug(f'{self.accounting_legs=}') + debug(f'{legs=}') rev = self.action.endswith('_REVERSE') - for l in self.accounting_legs: + for l in legs: if rev: l['acc_dir'] = DEBT if l['accounting_dir'] == CREDIT else CREDIT else: @@ -84,39 +114,55 @@ class Accounting: if l['amount'] is None: debug(f'amount is None:{l["amt_pattern"]}, {self.variable=},{self.caller.bill=}') raise AccountingAmountIsNone(self.caller.billid) + accounting_orgid = await self.caller.get_orgid_by_trans_role(sor, l, l.accounting_orgtype) + orgid = await self.caller.get_orgid_by_trans_role(sor, l, l.orgtype) + org1id = None if l.org1type is None else \ + await self.caller.get_orgid_by_trans_role(sor, l, l.org1type) + acc = await get_account(sor, accounting_orgid, orgid, l.subjectid, org1id=org1id) + if acc is None: + debug(f'can not get accountid {accounting_orgid=}, {orgid=},{l.subjectid=}, {org1id=}') + raise AccountIdNone(accounting_orgid, orgid, l['subjectid']) + l['accounting_orgid'] = accounting_orgid + l['orgid'] = orgid + l['org1id'] = org1id + l['accid'] = acc.id + l['balance_at'] = acc.balance_at + l['acc'] = acc + self.accounting_legs += legs def check_accounting_balance(self, legs): debt_balance = 0.0 credit_balance = 0.0 + curacc = None + acc_balance = 0.00 for l in legs: + if l['accid'] != curacc: + curacc = l['accid'] + acc_balance = l['acc'].balance + if l['acc_dir'] != l['balance_at']: + acc_balance -= l['amount'] + else: + acc_balance += l['amount'] + if acc_balance < 0.000000: + e = AccountOverDraw(curacc, acc['balance'], leg['amount']) + exception(f'{e},{legs=}') + raise e + l['new_balance'] = acc_balance if l['acc_dir'] == DEBT: debt_balance += l['amount'] else: credit_balance += l['amount'] - if abs(credit_balance - debt_balance) >= 0.01: + if abs(credit_balance - debt_balance) >= 0.00001: e = Exception('accounting legs not balance') exception(f'{legs=}, {e=}') raise e async def do_accounting(self, sor): self.sor = sor - await self.setup_accounting_legs() debug(f'do_accounting() ...{self.accounting_legs=}') self.check_accounting_balance(self.accounting_legs) for leg in self.accounting_legs: - accounting_orgid = await self.caller.get_orgid_by_trans_role(sor, leg, leg.accounting_orgtype) - orgid = await self.caller.get_orgid_by_trans_role(sor, leg, leg.orgtype) - org1id = None if leg.org1type is None else \ - await self.caller.get_orgid_by_trans_role(sor, leg, leg.org1type) - acc = await get_account(sor, accounting_orgid, orgid, leg.subjectid, org1id=org1id) - if acc is None: - debug(f'can not get accountid {accounting_orgid=}, {orgid=},{leg.subjectid=}, {org1id=}') - raise AccountIdNone(accounting_orgid, orgid, leg['subjectid']) - leg['accounting_orgid'] = accounting_orgid - leg['orgid'] = orgid - leg['org1id'] = org1id - leg['acc'] = acc await self.leg_accounting(sor, acc.id, leg) async def write_settle_log(self): @@ -137,52 +183,10 @@ class Accounting: sor = self.sor await sor.C('settle_log', ns) - async def overdraw_check(self, sor, accid, leg, tryAgain=True): - if accid is None: - raise AccountIdNone() - - sql0 = "select max(acc_date) as acc_date from acc_balance where accountid=${accid}$" - recs = await sor.sqlExe(sql0, {'accid':accid}) - acc_date = recs[0]['acc_date'] - bal = {} - if acc_date is not None: - if acc_date > self.curdate: - raise FutureAccountingExist(accid, self.curdate, acc_date) - ns={'accid':accid, 'acc_date':acc_date} - r = await sor.sqlExe("""select * from acc_balance -where accountid=${accid}$ - and acc_date = ${acc_date}$""", ns.copy()) - if len(r) > 0: - bal = r[0] - - accs = await sor.R('account', {'id':accid}) - if len(accs) == 0: - raise AccountNoFound(accid) - - acc = accs[0] - acc['acc_date'] = self.curdate - acc['balance'] = bal.get('balance', 0) - - if acc.get('balance') is None: - acc['balance'] = 0 - if acc['balance_at'] == DEBT and leg['acc_dir'] == CREDIT \ - or acc['balance_at'] == CREDIT and leg['acc_dir'] == DEBT: - if int(acc['balance']*10000) - int(leg['amount']*10000) < 0: - if tryAgain: - await asyncio.sleep(1.5); - return await self.overdraw_check(sor, accid, leg, tryAgain=False) - else: - print(f"{acc['balance_at']=}, {leg=}") - raise AccountOverDraw(accid, acc['balance'], leg['amount']) - leg['new_balance'] = acc['balance'] - leg['amount'] - else: - leg['new_balance'] = acc['balance'] + leg['amount'] - async def leg_accounting(self, sor, accid, leg): # print(f'leg_accounting(), {accid=}, {leg=}') - if leg['amount'] < 0.0001: + if leg['amount'] < 0.00001: return - await self.overdraw_check(sor, accid, leg) subjects = await sor.R('subject', {'id': leg['subjectid']}) if len(subjects) > 0: leg['subjectname'] = subjects[0].name diff --git a/accounting/getaccount.py b/accounting/getaccount.py index 52ad42b..a77cae2 100644 --- a/accounting/getaccount.py +++ b/accounting/getaccount.py @@ -5,15 +5,19 @@ from .const import * from accounting.accountingnode import get_parent_orgid async def get_account(sor, accounting_orgid, orgid, subjectid, org1id=None): - ss = "org1id is NULL" + ss = "a.org1id is NULL" if org1id: - ss = "org1id = ${org1id}$" + ss = "a.org1id = ${org1id}$" - sql = """select * from account - where - subjectid = ${subjectid}$ and - accounting_orgid = ${accounting_orgid}$ and - orgid = ${orgid}$ and + sql = """select +a.*, +case when c.balance is null then 0.00 else c.balance end as balance +from account a left join acc_balance b + on a.id = b.accountid +where + a.subjectid = ${subjectid}$ and + a.accounting_orgid = ${accounting_orgid}$ and + a.orgid = ${orgid}$ and """ + ss ns = { "accounting_orgid":accounting_orgid, diff --git a/accounting/init.py b/accounting/init.py index f3cb36c..e39ec02 100644 --- a/accounting/init.py +++ b/accounting/init.py @@ -16,12 +16,22 @@ async def all_my_accounts(request): userid = await env.get_user() userorgid = await env.get_userorgid() async with get_sor_context(request._run_ns, 'accounting') as sor: - sql = """select b.id, a.name, b.balance_at, c.balance from - subject a, account b, - (select a.* from acc_balance a, (select accountid, max(acc_date) max_date from acc_balance group by accountid) b where a.accountid=b.accountid and a.acc_date=b.max_date) c - where c.accountid = b.id - and b.subjectid = a.id - and b.orgid = ${orgid}$ + sql = """select +b.id, +a.name, +b.balance_at, +case when c.balance is null then 0.00 else c.balance end as balance +from + subject a, + account b left join + ( + select a.* + from acc_balance a, + (select accountid, max(acc_date) max_date from acc_balance group by accountid) b + where a.accountid=b.accountid and a.acc_date=b.max_date + ) c on c.accountid = b.id +where b.subjectid = a.id + and b.orgid = ${orgid}$ """ ns = {'orgid': userorgid} recs = await sor.sqlExe(sql, ns)