61 lines
2.9 KiB
Go
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
|
|
}
|