Files
aura-crm/production/backend/internal/cash/expense.go
T

61 lines
2.9 KiB
Go

package cash
import (
"context"
"github.com/jackc/pgx/v5"
)
// ValidMethod is RecordExpense/RecordPayroll callers' way to check a method
// against the same cash/card/invoice allowlist Create uses, before picking
// which of a (possibly multi-type) register's accepted types to draw down —
// exported since purchaseorder/payroll live in their own packages.
func ValidMethod(m string) bool { return validMethods[m] }
// RecordExpense inserts one expense row within an existing transaction —
// used by internal/purchaseorder's Receive so accepting a delivery and
// drawing the cost down from a chosen register commit atomically together.
// A tx-scoped package function other packages call into (not a Handler
// method bound to h.db), same shape as internal/inventory's ReceiveBatch —
// cash has no reason to ever import purchaseorder, so this keeps the
// dependency one-directional. registerID may be empty (no register chosen,
// e.g. a delivery received on credit with payment recorded later) — the
// row is still written, just without a register_id to draw down, and
// method in that case is whatever the caller defaults it to (Receive
// defaults to "cash"). The caller is responsible for checking method
// against the chosen register's own accepted types first (see
// cash.ValidMethod and registers.go's Update for the same membership
// check Create itself does) — this function just writes the row.
func RecordExpense(ctx context.Context, tx pgx.Tx, registerID, method, amount, note, staffID, staffName string) (string, error) {
var regID any
if registerID != "" {
regID = registerID
}
var id string
err := tx.QueryRow(ctx,
`INSERT INTO cash_transactions (type, method, amount, register_id, note, created_by_staff_id, created_by_staff_name)
VALUES ('expense', $2, $3::numeric, $1::uuid, $4, $5::uuid, $6)
RETURNING id`,
regID, method, amount, note, staffID, staffName,
).Scan(&id)
return id, err
}
// RecordPayroll is RecordExpense's sibling for internal/payroll's own
// payout step — same tx-scoped shape, but writes type='payroll' instead of
// 'expense' so it lands in cash_transactions' own separate payroll bucket
// (see internal/analytics' Revenue, which already sums the two types
// independently) rather than mixing into the generic expense ledger.
// registerID is required here (unlike RecordExpense's optional one) —
// payroll is always a real, immediate cash movement, never "on credit".
func RecordPayroll(ctx context.Context, tx pgx.Tx, registerID, method, amount, note, staffID, staffName string) (string, error) {
var id string
err := tx.QueryRow(ctx,
`INSERT INTO cash_transactions (type, method, amount, register_id, note, created_by_staff_id, created_by_staff_name)
VALUES ('payroll', $2, $3::numeric, $1::uuid, $4, $5::uuid, $6)
RETURNING id`,
registerID, method, amount, note, staffID, staffName,
).Scan(&id)
return id, err
}