Skip to content

v0.2.0 — PLC & unit-charge pricing; GST and collection correctness fixes

Choose a tag to compare

@AshishGTH AshishGTH released this 10 Aug 13:21
· 104 commits to master since this release

Upgrade note: upgrade-native.sh now syncs new PERMISSIONS rows to
existing installs automatically (see the permission-delivery fix below),
but it does not — and should not — grant them to any role for you. After
upgrading, an admin must open Admin → Roles, edit each role that
should get the new PLC/charge-management capability, check
inventory.unit.plc-manage / inventory.unit.charge-manage, and save.
This is a deliberate, permissions-only sync — see the entry below for why
auto-granting them would be its own bug, not a fix.

Fixed

  • Correctness fix affecting GST charged on bookings and extra charges —
    check your invoices if your company's GST State Code was ever unset.

    isIntraStateSupply() used to silently default to intra-state
    (CGST+SGST) whenever the company's GST state code or a booking's
    place-of-supply state code was missing, instead of raising an error.
    Any company whose Company Config GST fields were never filled in —
    every install created before those columns existed, or any install
    where nobody had visited Company Config yet — has been charging
    CGST+SGST on every booking and extra charge regardless of where the
    property actually is, which is the WRONG tax treatment (IGST) whenever
    the real place of supply is in a different state from the company's
    own. This produced no error and no warning; the only way to notice was
    to already know the correct treatment and check by hand. If this
    applies to you, review bookings/extra charges made while your GST
    config was incomplete and correct any wrongly-taxed invoices — this
    release does not retroactively fix already-issued invoices.
    Going
    forward, isIntraStateSupply() now throws instead of guessing — a
    booking or extra charge with incomplete GST config is rejected with a
    clear error naming what to set in Company Config, rather than silently
    taxed wrong. The app also now logs a warning at boot listing any
    company with incomplete GST config, and the staff admin UI shows a
    persistent banner linking to Company Config until it's completed.

  • Correctness fix affecting reported collection figures — upgrade and
    let the migration run before trusting any collection report.
    A prior
    bug (see the REPORTS-phase entry in CLAUDE.md) left bounced-cheque
    receipts still marked as collected (is_reversed = false) in every
    collection report, rollup, and the customer portal's own payment
    history — the code fix stops this going forward, but does nothing for
    receipts that already bounced before you upgrade. Migration
    20260804120000_backfill_bounced_receipt_is_reversed corrects those
    existing rows on your next prisma migrate deploy (part of the normal
    upgrade path — no manual step needed). It only touches the
    is_reversed/reversal_reason flag on affected receipts; it does not
    alter any ledger entry, allocation, or installment. If your reported
    collection totals looked too high before upgrading, they'll drop by
    the sum of any previously-bounced cheques once this runs.

  • A release that adds a PERMISSIONS constant never reached an
    existing install — only a fresh one.
    seed.ts's permission-upsert
    loop ran unconditionally, but the early-return gate right after it
    (if (existingCompany) return) meant no other seeded content, and
    critically no later permission addition, was ever re-applied to a
    company created in an earlier release. upgrade-native.sh now runs a
    dedicated sync-permissions.ts step (idempotent, permissions-table
    only) on every upgrade, so new permission rows reach existing installs
    the same way they reach fresh ones. Deliberately does NOT extend to
    masters or roles — see the upgrade note above and CLAUDE.md for why
    that's a different (and mostly correct-as-is) problem.

  • A system role (super_admin, company_admin, etc.) could never be
    granted a newly-added permission through the UI, at all.

    RolesService.update() rejected any change to a system role, not just
    a rename — so even with the fix above delivering the permission row,
    there was no way to actually grant it to a role. Scoped the guard to
    an actual name change only; a system role's permission set is now
    freely editable (its name and existence stay protected, as before).

Added

  • PLC and unit-charge management. Unit-level PLCs (park-facing,
    corner, etc.) and extra charges (IFMS, legal, etc.) can now be
    assigned per unit from the Inventory → Project → Pricing panel, and
    flow into a booking's cost breakup and the confirm step's total
    automatically. PLC amounts are snapshotted in paise at assignment time
    (from a percentage of the unit's rate, or a flat amount) and never
    retroactively change if the base rate is revised later.
  • Per-charge-type GST rates. Charge Types now carry their own
    optional GST rate and HSN/SAC code (Masters → Charge Types). A cost
    line's GST resolves in order: its own rate, then its charge type's
    rate, then the booking's base-line rate — never silently zero-rated.
    A PLC line has no charge type, so it always inherits the base line's
    rate; this is stated explicitly rather than left implicit.

Changed

  • Replaced the argon2 password-hashing dependency with
    @node-rs/argon2 (pure Rust via napi-rs). argon2 had caused two
    separate deployment failures on two different platforms (an
    Alpine/musl prebuild issue, and a still-unexplained SIGSEGV
    crash-loop on GitHub's ubuntu-latest CI runners — see CLAUDE.md
    for the full, ultimately-inconclusive investigation); the replacement
    ships prebuilt binaries for every platform this project targets and
    never falls back to compiling from source. Existing stored password
    hashes remain verifiable — both libraries use the same standard PHC
    string format, confirmed by directly cross-verifying a real hash
    between the two before switching. No action needed on upgrade, no
    password resets.