| Rev |
Age |
Author |
Path |
Log message |
Diff |
| 37591 |
44 m |
amit |
/trunk/profitmandi-web/src/main/java/com/spice/profitmandi/web/v2/controller/fofo/ |
Route V2 admin wallet adjustment through WalletServiceImpl
Byte-identical duplicate of the fofo /walletUpdate handler, with the same missing
row lock. Points at the shared walletService.applyManualAdjustment so both
modules share one code path. |
|
| 37590 |
44 m |
amit |
/trunk/profitmandi-fofo/src/main/java/com/spice/profitmandi/web/controller/ |
Route admin wallet adjustment through WalletServiceImpl
/walletUpdate hand-rolled the balance update and history row, bypassing the
service entirely, and read the wallet with selectByRetailerId instead of
selectByRetailerIdForUpdate - so unlike every other wallet path it took no row
lock, and a concurrent adjustment and purchase could lose an update.
Now calls walletService.applyManualAdjustment, which reuses getManualReference
for the reference allocation and routes the movement through
addAmountToWallet/consumeAmountFromWallet(forced). Behaviour is preserved: admin
debits stay forced, so they can still push a wallet negative as before. Removes
~45 duplicated lines. |
|
| 37589 |
44 m |
amit |
/trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/scheduled/ |
Drive credit limits off the investment sweep; stop phantom limit writes
Adds the 2-minute sweep schedule and points the limit recalculation at the
partners whose base_value actually moved, instead of rescanning all 980 every
20 minutes. Measured on production: 3 partners change per 2-minute window on
average, peak 18.
- ScheduledSkeleton: 2-min sweepPartnerInvestment, daily refreshAgedStockDaily
at 00:20; the 20-min full pass is kept as a backstop for inputs base does not
capture (risk, the SIDBI floor, hard_limit)
- BatchScheduledTasks.sweepPartnerInvestment: sweep, then limits for changed
bases only
- calculateChangedPartnerLimits(restrictTo) to support that
- scaleMoney(): round to paise before comparing and storing. The limit comes out
of a double multiply, so 65123.7400 round-tripped as 65123.740000000005 and
compareTo called it a change. 208 of 230 'changed' partners in one production
run differed by under half a paisa, rewriting sd_credit_requirement and
dtr.credit_account ~14,000 times a day for identical values and churning the
SIDBI mirror. Real changes were never below a rupee, so 2dp cannot suppress one.
- getFirstBillingDates: one grouped query instead of one per partner
Credit limits for 24 partners will rise on the first sweep after deploy - that is
the aged-Apple double-deduction correction landing (see r37588), not a defect.
NOTE: cron is a standalone Spring Boot jar; the new schedules only fire once it
is restarted with --spring.profiles.active=scheduled. |
|
| 37588 |
45 m |
amit |
/trunk/profitmandi-dao/src/main/ |
Serve partner investment from a 2-minute snapshot instead of a 3-hour cache
getInvestment() was @Cacheable on a 3-hour Redis cache that addAmountToWallet
evicted but consumeAmountFromWallet did not. A partner whose advance payment was
swept straight to a loan had the money counted twice - once as a still-cached
wallet balance, once as the reduced utilisation - overstating their credit limit
by the payment x their tier until the cache expired.
Replaced with fofo.partner_investment, refreshed every 2 minutes by
PartnerInvestmentSweepService. Every coupled term is read in one pass, so wallet
and utilisation (and in-stock and aged-Apple) can never come from different
moments. A shorter TTL would only have made the error rarer - it scales with the
payment, not the delay.
- PartnerInvestment entity/repository + partner_investment_snapshot.sql
- PartnerInvestmentSweepService: 9 batched reads, stores base_value for change
detection, refreshes aged-Apple for partners whose stock moved intra-day
- getInvestment/getInvestmentsForFofoStores both read the snapshot, so the two
paths no longer disagree; live compute retained as fallback
- selectActivatedStockAmountByFofoIds: batches an N+1 that cost 233ms x 1690
partners (6.6 min -> 1.0 s)
- getFirstBillingDates: batches another N+1 (4.6 s -> 0.67 s)
- selectPendingGrnOrders(List) now applies the same SD_START_DATE floor as the
single-partner overload; the two were reporting different GRN-pending
- selectPartnerStockValueMap takes excludeActivated: an activated Apple handset
held past the aging window was added once to in-stock and subtracted twice
(activated stock, then the aged haircut). 90 units, 24 partners, Rs 65.19 lakh
double-deducted. Live-demo passes false - no overlap there.
- applyManualAdjustment: routes the two hand-rolled admin wallet adjustments
through WalletServiceImpl so they take the FOR UPDATE lock
- add_idx_order_grn_pending.sql: covering index, GRN query 1320ms -> 514ms |
|
| 37587 |
2 h 23 m |
ranu |
/trunk/profitmandi-dao/src/main/java/com/spice/profitmandi/dao/model/ |
ticket download option given and some enhancement on notification panel |
|
| 37586 |
2 h 26 m |
ranu |
/trunk/ |
drr view modification |
|
| 37585 |
1 d 0 h |
amit |
/trunk/profitmandi-dao/src/main/java/com/spice/profitmandi/dao/repository/offers/ |
Fix web-offer sync: selectModelOffers join order (Unknown column 'o.id' in 'on clause')
selectModelOffers led its FROM with offers.offer_product p and then joined
offers.offer_raw_row r ON r.offer_id = o.id, referencing alias o one join
before o was introduced. An ON clause resolves only against tables already
in the join order, so MySQL rejected it with SQLSyntaxErrorException:
Unknown column 'o.id' in 'on clause'. Every per-model sync since r37581 has
therefore failed - webOfferSync=FAILED on the 15:04 and 17:14 re-ingests of
document 4 - leaving the 166 legacy NULL-key row-badges active and zero
consolidated badges published.
Reordered to lead with offers.offer o, matching the proven shape of the
sibling selectRowOffers query. All three are inner joins so the result set
is unchanged; BEST_OFFER_WINS and ORDER BY p.catalog_id still resolve.
Verified by executing the corrected statement read-only against prod for
document 4: 733 rows over 314 distinct catalog ids, the expected model
count. Not caught by tests because all 53 offercircular tests are
stub-based (no Spring, no DB) and never execute this SQL.
Failure was visible only because r37568 records webOfferSync= in
ingest_summary; the runner still swallows the exception by design. |
|
| 37584 |
1 d 2 h |
ranu |
/trunk/profitmandi-fofo/src/main/webapp/WEB-INF/views/ftl/ |
ticket download option given and some enhancement on notification panel |
|
| 37583 |
1 d 2 h |
ranu |
/trunk/profitmandi-fofo/src/main/webapp/WEB-INF/views/ftl/ |
ticket download option given and some enhancement on notification panel |
|
| 37582 |
1 d 3 h |
ranu |
/trunk/profitmandi-fofo/src/main/webapp/WEB-INF/views/ftl/ |
ticket download option given and some enhancement on notification panel |
|
| 37581 |
1 d 3 h |
amit |
/trunk/profitmandi-dao/src/main/ |
web offer sync: retire the pre-consolidation badges instead of deleting them
The sync now deactivates any CIRCULAR row with a NULL circular_offer_key on its next
run. A row-keyed badge is a fragment of what a consolidated badge says, so leaving it
active would show a partner the same offer twice - once whole and once in pieces.
That removes the DELETE from the migration entirely, which is strictly better:
* nothing is destroyed, so a bad sync is one UPDATE away from being undone
* web_offer_product has NO foreign key to web_offer, so the DELETE had to clear its
532 rows separately or strand them as orphans
* no window where a circular has no badges at all - the old ones stay live until the
new ones exist
The migration is now purely additive: one ALTER, safe to run ahead of the deploy, which
is how it was applied to prod. |
|
| 37580 |
1 d 3 h |
amit |
/trunk/ |
web offer: state an EMI scheme the circular gave without months
Rs.2,500 on Credit Card EMI (CIB) with IDFC First Bank, Kotak Mahindra Bank
52 Sep'26 offers have a bare 'CIB' tenure cell - the circular names the EMI type and
gives no months. TenureParser correctly yields no tenure row for a cell with no digits,
so the type vanished and the badge said only 'on Credit Card EMI'. Every one of those
52 carries a cashback, so it was 52 offers' worth of SKUs silent about the EMI they
apply to.
The type is stated because the circular stated it. The MONTHS are not invented,
because it did not give them - the same line the CIB expansion was left on.
Read from offer_raw_row.col_emi_tenure rather than stored: 'scheme, no month' is not a
tenure, and offer_tenure.tenure_months is NOT NULL, so persisting it would mean a fake
0 row standing for a fact that is not a tenure.
Only ever attached to an EMI mode, and real tenures always win over the bare code.
ModelOfferTest 8 -> 11. |
|
| 37579 |
1 d 4 h |
amit |
/trunk/profitmandi-fofo/src/ |
web offer: one badge per model, consolidating everything the circular grants it
Edge 60 Pro 12+256 carried five badges - a Full Swipe cashback, an EMI cashback and
three EMI schemes - and the partner's card listed all five. It now carries one:
SEPTEMBER 2026 Motorola Cashback Offer-2,500 On Credit Card EMI
& 2,000 On Credit Card Full Swipe
Models whose consolidated terms are IDENTICAL share a badge, so 314 models collapse to
~107 rows while every model still appears in exactly one.
Rules, from the taxonomy: any model has cashback on Full Swipe and/or EMI, instant or
deferred, plus NCE/LCE/CIB with tenures, and any of it may differ per bank.
- 'Upto' only where a mode carries several values - which is what per-bank pricing
looks like. One value is stated plainly.
- Highest value first.
- Banks ride with each line only when they DIFFER; one shared set is hoisted to a
single trailing 'Banks:' line. A third of models need the per-line form.
- An amount whose window is narrower than the badge's states its own end date. Apple
1025780 pays Rs.6,000 to the 16th then Rs.4,000 to the 26th; without this the badge
would advertise Rs.6,000 for ten days it is not available.
⚠️ Banks and tenures belong to the OFFER a line came from, never to the model. On Edge
60 Pro the no-cost EMI is ICICI on 3 and 6 months but SBI on 3 only, and the EMI
cashback is IDFC/Kotak with no tenure at all. Attaching the model's tenures to its
cashback would promise no-cost EMI on a bank that never offered it. This also corrects
an earlier reading of mine that called the SBI 3-month line a duplicate of ICICI's -
it is a different bank, not noise.
New ModelOfferTest, 8 cases, every one taken from a real Sep'26 row. |
|
| 37578 |
1 d 4 h |
amit |
/trunk/profitmandi-dao/src/main/ |
web offer sync: read what the circular grants each MODEL, not each row
selectModelOffers returns one row per (catalog_id, benefit), carrying the context that
qualifies it - the banks it applies to, its tenures and their scheme, the timing and
the window. Consolidation happens in the service; this is the read it needs.
LEFT JOIN on the benefit, because a scheme-only offer carries no cashback and dropping
it here would lose the NCE/LCE badge entirely.
Content-keyed writes alongside: selectSyncedByKey, insertKeyedWebOffer,
replaceProducts, deactivateMissingKeys. A badge is now identified by a sha256 of its
terms, so models with identical terms share one and a model appears in exactly one.
Migration add_web_offer_per_model.sql adds circular_offer_key + its unique key, and
drops the row-keyed CIRCULAR badges - one of those is a fragment of what a
content-keyed badge says and there is no mapping from five fragments to one whole, so
they are regenerated by the next ingest.
⚠️ web_offer_product has NO foreign key to web_offer, so its rows do not cascade and
are deleted explicitly or they are orphaned. web_offer_sync_shadow does cascade.
source='CIRCULAR' only - the 3,850 hand-authored rows are never touched. |
|
| 37577 |
1 d 4 h |
amit |
/trunk/profitmandi-fofo/src/ |
web offer: an EMI cashback names its tenures WITH their scheme
Rs.2,500 on Credit Card EMI (NCE 3, 6; LCE 9, 12 months)
A bare '3, 6, 9, 12 months' says which tenures earn the cashback but not which of
them are no-cost - and at the counter the two facts are only useful together.
The trailing scheme sentence is dropped when an EMI benefit already carried it, since
it would state the same thing twice. It stays when the cashback is on Full Swipe only:
EMI is still available, it just is not what the cashback is on.
Tenures whose scheme the circular never gave keep their months but carry no label -
the months are real, the scheme would be invented.
Tests 16 -> 18. |
|
| 37576 |
1 d 5 h |
ranu |
/trunk/profitmandi-fofo/src/main/webapp/WEB-INF/views/ftl/ |
ticket download option given and some enhancement on notification panel |
|
| 37575 |
1 d 5 h |
ranu |
/trunk/profitmandi-fofo/src/main/webapp/WEB-INF/views/ftl/ |
ticket download option given and some enhancement on notification panel |
|
| 37574 |
1 d 5 h |
amit |
/trunk/profitmandi-fofo/src/ |
web offer: print the EMI scheme code, not a guessed expansion
NCE / LCE / CIB are industry standard and now appear verbatim:
Instant cashback. Rs.3,000 on Credit Card EMI (3, 6 months);
Rs.3,000 on Credit Card Full Swipe. LCE on 3, 6 months. Banks: ...
The expansions were not ours to invent. emi_scheme.display_name carries 'Cashback
Instant Benefit' for CIB, and the evidence contradicts it: the Sep'26 cell
'3 NC 6,9,12 CIB' puts CIB in ONE ladder with NCE, so it describes who bears the
interest on a tenure rather than how a cashback is delivered - and 53 of 55 CIB rows
carry a separate cashback amount anyway, which that expansion would make redundant.
Pine Labs' own docs define ICB as Instant Cashback on UPI; the circular writes CIB 60
times and ICB never.
A guessed expansion on a partner badge risks stating the opposite of the truth - for
CIB, advertising a benefit where the customer may in fact be bearing the interest.
The code cannot be wrong that way.
A scheme is still stated ONLY when the circular gave one; tenures with no scheme are
described as nothing at all.
Also fixes the singular: 'NCE on 3 month', not '3 months'. |
|
| 37573 |
1 d 5 h |
ranu |
/trunk/profitmandi-fofo/src/main/webapp/WEB-INF/views/ftl/ |
ui look and feel color changing mode for drr |
|
| 37572 |
1 d 5 h |
amit |
/trunk/profitmandi-fofo/src/ |
web offer: state the whole offer in the title, and the EMI scheme in the detail
Title now states EVERY payment mode, highest first, in the house style of the
hand-written rows:
SEPTEMBER 2026 Samsung Cashback Offer-3000 On Credit Card Full Swipe
& 3000 On Credit Card EMI & 3000 On UPI
A partner scanning the card should see the whole offer, not just its best number.
Brand keeps its catalog casing (Samsung, not SAMSUNG). A scheme-only offer is still
titled by its scheme - calling it a cashback offer is what produced the live
'Cashback Offer- NA' badges.
Detail additions:
- the EMI scheme, when the circular gave one: '... Low Cost EMI on 3, 6 months.'
The circular states it in its own column ('3m,6m LCE' beside '3000 CC Full
Swipe/CC EMI/UPI') and a partner needs both halves.
- eligible tenures on an EMI benefit: 'Rs.3,000 on Credit Card EMI (3, 6 months)'.
Cashback 'on Credit Card EMI' is not actionable without knowing which tenures
qualify. Full Swipe and UPI carry no tenure, so they get none.
⚠️ A scheme is stated ONLY when the circular gave one. Tenures with no scheme are
described as nothing at all - naming them would read as a no-cost or low-cost claim
that was never made.
Tests 14 -> 16. |
|