Deepen Account + Stock modules significantly
Account (+400 LOC): - Accounting reports: Trial Balance, Balance Sheet, Profit & Loss, Aged Receivable/Payable, General Ledger (SQL-based generation) - account.report + account.report.line models - Analytic accounting: account.analytic.plan, account.analytic.account, account.analytic.line models - Bank statement matching (button_match with 1% tolerance) - 6 default report definitions seeded - 8 new actions + 12 new menus (Vendor Bills, Payments, Bank Statements, Reporting, Configuration with Chart of Accounts/Journals/Taxes) Stock (+230 LOC): - Stock valuation: price_unit + value (computed) on moves and quants - Reorder rules: stock.warehouse.orderpoint with min/max qty, qty_on_hand compute from quants, action_replenish method - Scrap: stock.scrap model with action_validate (quant transfer) - Inventory adjustment: stock.quant.adjust wizard (set qty directly) - Scrap location seeded Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1414,10 +1414,11 @@ func initAccountBankStatement() {
|
||||
)
|
||||
|
||||
// Bank statement line
|
||||
orm.NewModel("account.bank.statement.line", orm.ModelOpts{
|
||||
stLine := orm.NewModel("account.bank.statement.line", orm.ModelOpts{
|
||||
Description: "Bank Statement Line",
|
||||
Order: "internal_index desc, sequence, id desc",
|
||||
}).AddFields(
|
||||
})
|
||||
stLine.AddFields(
|
||||
orm.Many2one("statement_id", "account.bank.statement", orm.FieldOpts{String: "Statement"}),
|
||||
orm.Many2one("move_id", "account.move", orm.FieldOpts{String: "Journal Entry", Required: true}),
|
||||
orm.Many2one("journal_id", "account.journal", orm.FieldOpts{String: "Journal", Required: true}),
|
||||
@@ -1434,7 +1435,50 @@ func initAccountBankStatement() {
|
||||
orm.Integer("sequence", orm.FieldOpts{String: "Sequence"}),
|
||||
orm.Char("internal_index", orm.FieldOpts{String: "Internal Index"}),
|
||||
orm.Boolean("is_reconciled", orm.FieldOpts{String: "Is Reconciled"}),
|
||||
orm.Many2one("move_line_id", "account.move.line", orm.FieldOpts{String: "Matched Journal Item"}),
|
||||
)
|
||||
|
||||
// button_match: automatically match bank statement lines to open invoices.
|
||||
// Mirrors: odoo/addons/account/models/account_bank_statement_line.py _find_or_create_bank_statement()
|
||||
stLine.RegisterMethod("button_match", func(rs *orm.Recordset, args ...interface{}) (interface{}, error) {
|
||||
env := rs.Env()
|
||||
for _, lineID := range rs.IDs() {
|
||||
var amount float64
|
||||
var partnerID *int64
|
||||
err := env.Tx().QueryRow(env.Ctx(),
|
||||
`SELECT COALESCE(amount::float8, 0), partner_id FROM account_bank_statement_line WHERE id = $1`, lineID,
|
||||
).Scan(&amount, &partnerID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("account: read statement line %d: %w", lineID, err)
|
||||
}
|
||||
|
||||
if partnerID == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// Find unreconciled move lines for this partner with matching amount
|
||||
var matchLineID int64
|
||||
env.Tx().QueryRow(env.Ctx(),
|
||||
`SELECT l.id FROM account_move_line l
|
||||
JOIN account_move m ON m.id = l.move_id AND m.state = 'posted'
|
||||
JOIN account_account a ON a.id = l.account_id
|
||||
WHERE l.partner_id = $1
|
||||
AND a.account_type IN ('asset_receivable', 'liability_payable')
|
||||
AND ABS(COALESCE(l.amount_residual::float8, 0)) BETWEEN ABS($2) * 0.99 AND ABS($2) * 1.01
|
||||
AND COALESCE(l.amount_residual, 0) != 0
|
||||
ORDER BY ABS(COALESCE(l.amount_residual::float8, 0) - ABS($2)) LIMIT 1`,
|
||||
*partnerID, amount,
|
||||
).Scan(&matchLineID)
|
||||
|
||||
if matchLineID > 0 {
|
||||
// Mark as matched
|
||||
env.Tx().Exec(env.Ctx(),
|
||||
`UPDATE account_bank_statement_line SET move_line_id = $1, is_reconciled = true WHERE id = $2`,
|
||||
matchLineID, lineID)
|
||||
}
|
||||
}
|
||||
return true, nil
|
||||
})
|
||||
}
|
||||
|
||||
// -- Helper functions for argument parsing in business methods --
|
||||
|
||||
Reference in New Issue
Block a user