Commit Graph
15 Commits
Author SHA1 Message Date
aefril d3e3ea6b08 Merge pull request 'feat: add printer types' (#40) from fix/point-payment-method-types into main
Reviewed-on: https://gits.altru.id/apksel-dev/apskel-pos-backend/pulls/40
2026-10-01 17:43:53 +02:00
efrilm b0ef226af1 feat: add printer types 2026-10-01 22:26:44 +07:00
efrilmandClaude Opus 5.5 f16a5da951 docs(customer): mobile app guide, outlets, order history and registration
Adds docs/mobile-customer-enakpoint.md, written as a brief for building the
customer app: the UI rules, the API conventions, each screen with its
requests and responses, push handling, PIN flows, paying at the cashier,
exchange, transfer, games, what was removed, and a checklist. It covers the
new GET /customer/outlets, GET /customer/orders and /customer/orders/:id,
and the optional organization_id at registration, which the API reference
now lists too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 19:39:52 +07:00
efrilmandClaude Opus 5.5 c988a79d3b refactor(loyalty): remove what is left of tokens
Tokens are EnakCoin and no app uses the token names any more, so their
compatibility layer goes:

- GET /customer/tokens and its handler, service, processor and response
  types.
- total_tokens and tokens_history on GET /customer/wallet; last_updated
  now comes from the most recent row of either currency.
- token_used and tokens_remaining on game and spin responses, and
  sort_by=token_used on the game play list.
- TOKENS as a campaign type and reward type, with the mapping to COINS:
  migration 000092 already renamed the stored values.

The customer_tokens table and its entity stay, as cmd/wallet-migrate still
reads them, and LEGACY_TOKENS stays as the reference of the MIGRATION rows
it wrote. The docs list the removed names and their replacements.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 16:55:27 +07:00
efrilm 582dc75543 Reapply "feat(loyalty): EnakPoint & EnakCoin" (#32)
This reverts commit 4e24f9bbb0.
2026-09-30 15:31:44 +07:00
efrilmandClaude Opus 5.5 4e24f9bbb0 Revert "feat(loyalty): EnakPoint & EnakCoin" (#32)
This reverts merge commit 645da30, returning main to f0ff59f.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 15:16:15 +07:00
efrilmandClaude Opus 5.5 ae8003c1e4 docs(loyalty): API reference and backoffice guide for EnakPoint & EnakCoin
Adds docs/api-enakpoint.md, the endpoint reference for the customer app,
POS and dashboard, and docs/backoffice-enakpoint.md, the screens the
backoffice needs: outlet and organization settings with the save flow and
impact dialog, both expiry models, the customer wallet with adjustment and
trace, PIN removal and security log, settings history, game coin_cost and
the EnakPoint payment method.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 15:01:02 +07:00
efrilmandClaude Opus 5.5 3cd88a55c8 docs(loyalty): EnakPoint & EnakCoin integration guide
Adds docs/integration-enakpoint.md for the customer app, POS and dashboard
teams (PC-602), in the style of the weight-based products guide.

It covers the response envelope and error codes, balances and history with
every ledger type, the expiring list and FCM push types with their data,
the PIN flows and the four PIN error codes, paying with EnakPoint at the
cashier (payment code, preview, POST /payments) and in the app, void and
refund rules, exchange and transfer with Idempotency-Key, games on
EnakCoin, the deprecated endpoints and fields with their replacements, the
dashboard's outlet and organization settings including both expiry models,
the customer wallet, adjustments, trace and PIN removal, and a checklist
per team.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 14:46:06 +07:00
efrilmandClaude Opus 5 ebf666c004 feat(product): require a unit for weight-based products
A product sold by weight with no unit produces order lines with nothing to
print: the receipt would read "4,2" with no idea of what. Until now nothing
stopped that — the mistake only surfaced at the cashier.

Enforce it in two places, because neither alone sees the whole picture. On
create, the validator has everything it needs. On update, the request may
omit unit_id for a product that already has one, so the check runs in the
processor against the merged product: what is rejected is the end state, a
product sold by weight with no unit.

Also fixes two things this uncovered:

The struct tags on the product contracts are decorative — this validator is
hand-written and never calls validator.Struct — so `oneof=unit weight` was
never enforced, and an unknown sell_by was silently rewritten to "unit" by
the mapper. It is now rejected with a message that names the valid values.

The update validator's "at least one field" guard did not list unit_id,
sell_by or print_to_checker, so an update carrying only one of those was
turned away as an empty request.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 17:29:34 +07:00
efrilmandClaude Opus 5 d3987c7114 docs(order): add weight-based product integration guide
Client-facing companion to the RFC, aimed at the POS Mobile and Backoffice
teams: endpoints and payloads for setting up a weight product, placing an
order, rendering the line, and voiding, refunding or splitting it.

Documents two gaps the teams have to work around rather than discover:
unit_id is not yet enforced when sell_by is "weight", so Backoffice must
require it in the form; and money rounds to 2 decimals rather than whole
rupiah, which is still an open decision.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 17:20:22 +07:00
efrilmandClaude Opus 5 992bb04816 feat(order): support weight-based products
Products like fish are sold per weighing (4.2 ons, 5.6 ons), which the
order line could not represent: quantity is INTEGER and prices are always
computed as quantity * unit_price.

Model one weighing as one order line. quantity stays INTEGER and keeps
meaning "how many items"; the measured amount goes into a new nullable
order_items.weight, and the line is priced weight * unit_price. Two
weighings of the same product are two lines, never merged into one.

Keeping quantity integral avoids float comparisons in void, refund and
split bill, where accumulated rounding error would silently misbehave —
"1.4 + 1.4 + 1.4" is not 4.2 in float64, which would leave a fully paid
split-bill item marked unpaid.

BillableQuantity() is now the single place that decides between weight
and count; every price and cost calculation goes through it. Missing one
would bill a 4.2 ons fish as a single ons — wrong money, no error.

Two database constraints back the design: a weighed line always carries a
positive weight, and its quantity is pinned to 1. The latter also makes
void all-or-nothing for weighed lines, so the row-splitting branch can
never produce a zero-weight remainder row.

Also wires product.unit_id through the API, which was previously not
settable at all, and corrects the misleading comment on the request's
unit_price field — that value has never been used; price always comes
from the database.

Design notes and the audit of every price multiplication site are in
docs/rfc-weight-based-products.md.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 17:14:42 +07:00
aditya.siregar 4f5950543e init 2025-07-18 20:10:29 +07:00
aditya.siregar 1201b2e45b Add void print 2025-06-24 02:47:44 +07:00
aditya.siregar 764ea68bf9 Update Payment VA 2024-10-15 11:52:34 +07:00
aditya.siregar 67f1dbc850 init project 2024-05-28 14:14:55 +07:00