| Rev |
Age |
Author |
Path |
Log message |
Diff |
| 37475 |
1 m |
amit |
/trunk/profitmandi-web/src/main/java/com/spice/profitmandi/web/config/ |
mail: delete the dead SendGrid bean from web, make the authenticated identity primary
Removes the live SendGrid bean plus two commented-out corpses (an old
build@shop2020.in sender and a dead alias). googleMailSender is now @Primary and
answers to 'mailSender', so unqualified injections get the working authenticated
sender instead of one that rejects with 535. |
|
| 37474 |
1 m |
amit |
/trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/ |
mail: delete the dead SendGrid bean from cron, make the authenticated identity primary
The @Primary bean named 'mailSender' was SendGrid, so every unqualified
JavaMailSender injection got it -- and SendGrid rejects with 535, which is what
was failing 10 report/notification cron jobs (dailyTrackingReport,
monthlyTargetForPartner, sendFeebackSalesAndRbm and others). 420 occurrences in
one log.
googleMailSender now answers to both 'googleMailSender' and 'mailSender' and is
@Primary, so those injections resolve to a sender that works and whose From is
rewritten to the authenticated identity. No call site changes. |
|
| 37473 |
1 m |
amit |
/trunk/profitmandi-dao/src/main/java/com/spice/profitmandi/service/mail/ |
mail: retire the SENDGRID sender label
SendGrid was never actually used by the outbox -- resolveSender() already fell
through to the Workspace relay for it, which is why outbox mail kept working
while direct JavaMailSender injections failed.
Two senders remain, each named for what it is:
GOOGLE - authenticates as sdtech@smartdukaan.com; AuthenticatedIdentityMailSender
rewrites every From to that identity, because Workspace binds an
authenticated session to one address and rejects any other with
'535 Authorization failed: Authenticated user is not authorized to send mail'
RELAY - Workspace relay, no auth, authorised by source IP, may send as noreply@
Default convenience methods now route to RELAY explicitly rather than through a
label that named a provider we do not use. |
|
| 37472 |
1 h 4 m |
amit |
/trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/ |
IMEI activation: one snapshotted daily pass per brand, on one thread, with per-brand metrics
Four @Scheduled jobs every 5 minutes become two daily passes. Oppo and realme
share one thread and alternate in 25-imei chunks, so exactly one ChromeDriver is
alive at a time instead of four; vivo keeps its own thread since it is direct
HTTP and does not contend for a browser.
The pass snapshots its pool before any browser starts and walks that list to the
end. It never re-queries, and that is the actual fix. A failed lookup never
reaches dateMap.put, so no row is written, so createTimestamp is not bumped, so
the imei was eligible again on the next tick five minutes later. Measured 29-Aug:
realme issued 4,524 requests against 1,004 distinct imeis -- 4.5 asks each, 78%
of the day's budget spent re-asking -- while oppo, which rarely fails, sat at
1.03. More requests hardened the block, which caused more failures. A pass bounds
that: a failure costs one retry tomorrow, never one in five minutes.
This supersedes the r37447/r37448/r37449 argument about driver count, which was
about the wrong variable. That argument blamed realme's collapse on CPU
contention pushing the captcha render past the element waits. The logs do not
support it: on 29-Aug oppo took ZERO canvas timeouts across all 24 hours on the
same box, same six cores, same driver count, same captcha vendor, load average
0.9 -- including the 15:00-23:00 window in which realme solved nothing at all.
Realme's own canvas wait is 15s against oppo's 8s, so the longer wait is the one
expiring. What realme's timeout rate tracks is its own daily request volume, and
it resets at midnight: 920/day -> 0.3%, 3,467/day -> 28%, 4,524/day -> 75%. That
is realme.com declining to serve the widget.
DAYS=0 is deliberate and is not an off-by-one: the pool filter is
createTimestamp < now().atStartOfDay().minusDays(DAYS), so DAYS=1 measures
against yesterday midnight and silently yields a two-day cadence, which is what
oppo and realme were running.
Sizing measured on prod for a midnight start: oppo 4,133 and realme 2,118 imeis,
11.7h + 8.4h = 20.1 hours of a single thread. It fits with no slack; if the
'pass finished' counts come in short of 'pass starting', the lever is DAYS=1
rather than a second thread.
Observability: ImeiActivationGauges publishes the funnel per brand on
/actuator/prometheus, which alloy already scrapes on this host -- due, churned,
captcha_shown, captcha_solved, answered, dates_found, errors, run_seconds and
last_finish_epoch. Each stage fails differently and says what broke. Rates are
left to PromQL. The stage that matters for health is answered: churned>0 with
answered==0 is precisely the shape of both silent outages this year (oppo wrote
nothing for a week; the vivo captcha solver was dead for 46 days). dates_found is
deliberately NOT a health signal -- when the multi-year backlog drained at the
end of August, yield fell from ~100% to 2-3% on the same day across all three
brands with nothing broken.
Nagios cleanup: the Nagios server and every NRPE daemon are gone, so
WriteToPropertiesFile and the commented-out blocks that fed
nagios-cron.properties are deleted, and NagiosMonitorTasks is renamed
BalanceMonitorTasks for the transport it actually uses. Noted there that nothing
calls it -- there is no @Scheduled entry and no other caller -- which is why both
balance gauges have always read -1. |
|
| 37471 |
1 h 5 m |
amit |
/trunk/profitmandi-dao/src/main/java/com/spice/profitmandi/dao/ |
IMEI activation pools: skip stock billed today or yesterday
Both pool named queries now exclude serials whose sale is inside the last 48
hours -- o.billingTimestamp for the secondary path, fo.createTimestamp for the
tertiary one. A handset sold in the last two days has essentially never been
activated yet, so the lookup is spent for nothing: measured on prod, the
sold-within-180-days cohort returns an activation date on 2.4% (vivo) to 8.4%
(oppo) of lookups, against 43-97% for stock sold over a year ago.
billedBefore is set in ActivatedImeiRepositoryImpl rather than passed by the
caller, so no method signature changes and every brand inherits it -- vivo,
oppo, realme and motorola all draw from these two queries and nothing outside
profitmandi-cron uses them.
This is correctness rather than capacity: it trims 30 imeis from the oppo pool
and 9 from realme. |
|
| 37470 |
1 h 22 m |
amit |
/trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/ |
Remove dead Itel/Tecno SAP activation services
Both services call SAP OData endpoints that Transsion decommissioned on 2024-11-20:
ItelImeiActivationService -> cms.itel-mobile.com:8099/.../ZTERTIARY_SALES_REPRT_SRV
TecnoImeiActivation -> cms.tecno-mobile.com:8099/.../ZTERTIARY_SALES_REPRT_SRV
Both hosts now refuse connections outright (verified from the prod app server).
That decommissioning is what stopped Itel and Tecno activation ingest within the
same hour on 2024-11-20; everything since has arrived via manual CSV upload.
Neither service has produced a row in 21 months and neither can again.
Removed:
- both service classes
- ScheduledTasks.checkItelImeiActivation / .checkTecnoImeiActivation, their
@Autowired fields and imports (the only callers)
- the --checkItelImeiActivation / --checkTecnoImeiActivation startup args
Unaffected: ItelImeiActivationNewService and checkItelImeiActivationNew, which
target the current imwav portal and were fixed in r37467. |
|
| 37469 |
3 h 32 m |
ranu |
/trunk/profitmandi-fofo/src/main/webapp/WEB-INF/views/ftl/ |
rbm drr dashboard , all today po rbm view maped |
|
| 37468 |
3 h 58 m |
ranu |
/trunk/profitmandi-fofo/src/main/webapp/WEB-INF/views/ftl/ |
rbm drr dashboard new ui looks |
|
| 37467 |
5 h 42 m |
amit |
/trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/itelImeiActivation/ |
Fix Itel/Tecno activation pull: live session tokens, correct credentials, per-account isolation
The imwav pull has never worked since it replaced the SAP feed. Three defects:
- Login response was discarded and the listing call sent p-auth/utoken values
captured by hand in Jul-2023, so every request went out with an expired session.
Now takes utoken/urToken from the login response.
- Itel (IN404338I) carried a stale password (Dukaan@1234), which is why its pull
started failing Feb-2026. Corrected, and the two missing Tecno branch accounts
(Noida IN411226T, Lucknow IN404339T) added - the code previously knew only
IN404338T, which has no enterprise scope and returns nothing.
- Existing rows were looked up by the activation date string instead of the IMEI.
serial_number is the primary key, so the lookup always missed and re-persisted a
row that already existed, colliding on duplicate key on any re-pull.
Also pulls each account independently so one dead login cannot abort the remaining
accounts, and fails loudly when the portal rejects a login or the report call
instead of proceeding into a confusing downstream error. |
|
| 37466 |
6 h 2 m |
amit |
/trunk/profitmandi-fofo/src/main/webapp/WEB-INF/views/ftl/ |
Show aged Apple stock on the partner Total Investment breakdown
Adds an 'Apple Stock More Than 30 Days' row below 'Unbilled Activated Stock'
so the reason a partner's suggested credit limit dropped is visible. Display
only -- the figure is not deducted from the other rows.
Value comes from partnerStat.tmp, so it reads 0 until the cron that rebuilds
that file has run against the new build. |
|
| 37465 |
6 h 2 m |
amit |
/trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/scheduled/ |
Exclude aged Apple stock from investment when suggesting partner credit limits
Apple handsets a partner has held more than 30 days from GRN no longer count
toward the investment that drives the suggested limit. Applied in
getSuggestedAmount via getCreditableInvestment, covering the SDDIRECT,
hundred-percent and SIDBI branches. The aged amount is logged alongside each
limit change so a drop can be traced.
Scoped to the limit calculation only -- getTotalInvestment() is untouched, so
checkout payment options, the investment-OK gates and partner-facing screens
see the same stock value as before.
Measured on prod at time of commit: 580 units / ~3.94 Cr across 88 partners. |
|
| 37464 |
6 h 3 m |
amit |
/trunk/profitmandi-dao/src/main/java/com/spice/profitmandi/ |
Track aged Apple stock on partner investment; add category filter to stock value lookup
selectPartnerStockValueMap now takes a nullable categoryId so the same query
serves both the existing Live Demo exclusion (null = all categories) and the
new Apple handset lookup (category 10006). Existing callers pass null and are
behaviourally unchanged.
PartnerDailyInvestment carries agedAppleStockAmount, populated in both the
single and batch investment paths. It is JPA-@Transient, so no schema change,
and it still travels through the partnerStat.tmp serialization the FOFO
investment screen reads. Deliberately not part of getTotalInvestment() -- the
deduction is a credit-limit policy, so stock value is unchanged for checkout,
the investment-OK gates and partner-facing screens. |
|
| 37463 |
6 h 3 m |
amit |
/trunk/profitmandi-common/src/main/java/com/spice/profitmandi/common/model/ |
Add aged-stock constants for Apple credit-limit haircut
Apple handsets held by a partner beyond AGED_STOCK_APPLE_DAYS (30) days
from GRN are excluded from the investment used to suggest a credit limit. |
|
| 37462 |
6 h 5 m |
amit |
/trunk/profitmandi-dao/src/main/java/com/spice/profitmandi/dao/event/ |
Fire TagListing Solr update after commit instead of inside the write transaction
TagListingChangeListener used a plain @EventListener, so the async Solr rebuild
ran on a separate thread in a new transaction while the publishing controller
transaction was still open. FofoSolr.updateSingleCatalog then re-read
tag_listing on a connection that could not see the uncommitted rows and
re-indexed the pre-change price; for a catalog's first-ever listing it read
zero rows, took the isEmpty branch and deleted the doc from Solr.
Switched to @TransactionalEventListener(phase = AFTER_COMMIT). All publish
sites (TagListingController, PriceDropController, IndentController,
InventoryController, ModelHotDealServiceImpl) are inside a @Transactional
boundary, so no event is dropped. |
|
| 37461 |
2 d 0 h |
ranu |
/trunk/profitmandi-dao/src/main/java/com/spice/profitmandi/dao/model/ |
rbm drr dashboard new ui looks |
|
| 37460 |
2 d 0 h |
ranu |
/trunk/ |
rbm drr dashboard new ui looks |
|
| 37459 |
2 d 2 h |
amit |
/trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/scheduled/ |
Motorola IMEI activation: secondary + tertiary in one browser session
Mirrors the oppo/realme combined jobs. MotorolaImeiActivationService drives
CheckMotorolaWarrantyTask, with MotorolaChallengeSolver for the challenge.
Cadence comes from the pool query, which defers an imei for `days` after each
attempt (saveActivation bumps createTimestamp even when no date came back), so
days=2 retries everything every two days. Pending pool measured 1,534
(1,182 secondary + 352 tertiary); at ~10-14s/imei, 60 per invocation is about
12 minutes of driver time and clearing the pool inside 48h needs roughly 26
invocations, i.e. an OS cron entry every 90 minutes.
Do NOT schedule it inside the oppo/realme window: each driver tree costs
~850MB and this box has been OOM-killed twice with tomcat the victim, so peak
concurrent drivers is the number that matters. |
|
| 37458 |
2 d 3 h |
aman |
/trunk/profitmandi-fofo/src/main/ |
LOI payment screenshot: report upload failures instead of reporting the field as missing
Submit validated the hidden #payment-sc-docId, but a file input keeps displaying the
chosen filename regardless of what happens next - so a failed /document-upload left the
field looking attached to the operator and missing to the validator ("The following
fields are required: Payment screenshot" on a form that was in fact filled in).
The failure was silent three ways: doAjaxUploadRequestHandler had no error handler; the
global ajaxError reporter calls bootbox, which loads from cdnjs and throws if that is
blocked; and the confirm('Confirm file upload ?') gate returned false with no trace once
Chrome's "prevent this page from creating additional dialogs" box was ticked.
- common.js: optional error callback on doAjaxUploadRequestHandler (additive, opts out of
the generic global dialog via suppressGlobalError); uploadErrorMessage() extracts the
server's real reason from the JSON body or the plain-text duplicate-request body;
uploadDocument() pre-checks size/type, verifies a document_id actually came back, and
reports a message naming the file - .HEIC and .webp get specific guidance;
showAlert() falls back to native alert when bootbox is absent.
- loi-form.js: drop the confirm() gate, 'input' -> 'change', and clear the file input
whenever the id does not land so the widget can never claim an unuploaded file. Status
line under the field (Uploading... / Attached: name / Not attached). Submit now
distinguishes not-picked, still-uploading and upload-failed. Same treatment for the
OnboardingDocumentTable and mk_brand-fee-collection-details uploads, and the brand-fee
dialog names the Attachment field instead of echoing the raw form key.
- loi-form.vm, pendingForm.vm: accept="..." on both file inputs plus the status span.
- FileUploadController: reject empty files, report actual size against the actual limit,
and resolveExtension() falls back to the filename when the browser sends a generic MIME
type (Android file managers send application/octet-stream for ordinary JPG/PNG/PDF).
- AppConfig: static resource version 395 -> 396 for the JS change. |
|
| 37457 |
2 d 9 h |
amit |
/trunk/profitmandi-fofo/src/main/ |
Lock the KYC state to the GSTIN on the LOI
The LOI already captures and validates a GSTIN, and the first two characters of a
GSTIN are its GST state code - so once the LOI exists the KYC state is a fact
about that number, not something to re-type. When one is present the state
dropdown is prefilled from it and locked; otherwise it stays a normal
master-backed select.
Derived from the GSTIN rather than loi_form.gstState: that column is typed by
hand and holds values like 'UP', 'UTTAR PARDESH' and 'MADHAY PARDESH' which
resolve against nothing, and it is blank on 3 rows. Checked against prod - all
693 GSTINs on record are 15 characters and every one resolves through
statemaster.stateCode, and in all 14 rows where the two disagree the GSTIN is
right and the typed value is wrong or empty.
The lock is per row, not per page: the KYC screens list many onboardings, so the
derived state is passed as a map keyed by onboarding id. Blank rows carry their
onboarding id so a new KYC entry is locked too, which is the case that matters.
data-gst-locked keeps the row's dblclick edit toggle from re-enabling the field;
the value still submits because the JS reads it explicitly with .val(). |
|
| 37456 |
2 d 9 h |
amit |
/trunk/ |
Block billing when NIC rejects the transporter GSTIN for e-way bills
NIC files the e-way bill alongside the IRN, so a deregistered transporter
GSTIN returns Status=1 with InfoDtls[InfCd=EWBERR] (3029 "GSTIN - ... is
not active"): the IRN is valid while ewb_no stays null. Nothing downstream
reads that as a failure, so invoices kept being issued for goods that
could not legally move.
Cache the rejected transporter GSTIN in Redis and refuse to bill through
it. The block is keyed on the GSTIN, since one GSTIN is shared by several
warehouse_provider rows, and it carries the day it was raised so it lapses
at midnight and each new day re-tests NIC once. Correcting the GSTIN in
the provider panel lifts it immediately.
Only errors that are the transporter's fault block billing - NIC's 3029,
or any message naming the GSTIN we sent as TransId. Every other EWBERR
behaves as before: the IRN is filed and the e-way bill is retried later.
Guard sits in addBillingDetailsForGrouppedOrders before the pessimistic
lock and before any mutation, mirroring LogisticsServiceImpl#getEwbDetails
(order's own warehouse; self-pickup and runner skipped, as they travel on
a vehicle number rather than a transporter id). |
|