<?xml version="1.0" encoding="utf-8"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>SmartDukaan &#x2013; /</title><description>WebSVN RSS feed &#x2013; SmartDukaan</description><lastBuildDate>Mon, 31 Aug 2026 18:32:53 +0530</lastBuildDate><generator>WebSVN 2.8.6-DEV</generator><language>en</language><link>https://svn.smartdukaan.com/log.php?repname=SmartDukaan&amp;path=%2F&amp;max=40&amp;</link><atom:link href="https://svn.smartdukaan.com/rss.php?path=%2F&amp;repname=SmartDukaan" rel="self" type="application/rss+xml" />
<item><pubDate>Mon, 31 Aug 2026 18:32:25 +0530</pubDate><dc:creator>amit</dc:creator><title>Rev 37486 – mail: fix prod outage -- MailOutboxService must not implement MailQueue ...</title><description>&lt;div&gt;&lt;strong&gt;amit – 2 file(s) modified&lt;/strong&gt;&lt;br/&gt;mail: fix prod outage -- MailOutboxService must not implement MailQueue&lt;br /&gt;
&lt;br /&gt;
r37478 added &apos;implements MailQueue&apos; to MailOutboxService. That took down both&lt;br /&gt;
ROOT.war and profitmandi-web.war on the 18:19 deploy today; every route 404&apos;d&lt;br /&gt;
because neither Spring context would start.&lt;br /&gt;
&lt;br /&gt;
MailOutboxService has @Transactional methods, so Spring has to proxy it, and&lt;br /&gt;
@EnableTransactionManagement in both WebDBContextConfigure classes runs with the&lt;br /&gt;
default proxyTargetClass=false. While the class implemented no interface Spring&lt;br /&gt;
proxied it with a CGLIB subclass, which is still a MailOutboxService. Adding an&lt;br /&gt;
interface switched it to a JDK proxy implementing only MailQueue, so all 39&lt;br /&gt;
sites that inject the concrete MailOutboxService failed:&lt;br /&gt;
&lt;br /&gt;
  BeanNotOfRequiredTypeException: Bean named &apos;mailOutboxService&apos; is expected to&lt;br /&gt;
  be of type MailOutboxService but was actually of type com.sun.proxy.$Proxy1519&lt;br /&gt;
&lt;br /&gt;
The interface moves to a new MailQueueAdapter, which delegates to&lt;br /&gt;
MailOutboxService and carries no transactional annotations. MailQueueHolder and&lt;br /&gt;
EmailServiceImpl inject it by the MailQueue interface, so Utils.sendMail* and&lt;br /&gt;
EmailService still reach the outbox unchanged. MailOutboxService goes back to&lt;br /&gt;
implementing nothing, and its ~112 existing call sites are untouched.&lt;br /&gt;
&lt;br /&gt;
Chose this over proxyTargetClass=true, which would have flipped proxying for&lt;br /&gt;
every interface-implementing service in the app -- too broad for a hot fix.&lt;br /&gt;
&lt;br /&gt;
Verified with javap inside both built WARs: MailOutboxService declares no&lt;br /&gt;
interfaces, MailQueueAdapter implements MailQueue.&lt;br /&gt;
&lt;br /&gt;
Note for cron: it shares the dao jar, so a rebuild picks this up. Do not restart&lt;br /&gt;
cron on trunk without rebuilding it first.&lt;/div&gt;~ /trunk/profitmandi-dao/src/main/java/com/spice/profitmandi/service/mail/MailOutboxService.java&lt;br /&gt;+ /trunk/profitmandi-dao/src/main/java/com/spice/profitmandi/service/mail/MailQueueAdapter.java&lt;br /&gt;</description><link>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37486</link><guid>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37486</guid></item>
<item><pubDate>Mon, 31 Aug 2026 18:17:30 +0530</pubDate><dc:creator>amit</dc:creator><title>Rev 37485 – carlcare: enable tecno alongside itel  Itel went first because ...</title><description>&lt;div&gt;&lt;strong&gt;amit – 1 file(s) modified&lt;/strong&gt;&lt;br/&gt;carlcare: enable tecno alongside itel&lt;br /&gt;
&lt;br /&gt;
Itel went first because it is the brand we can CHECK -- the DCR pull independently&lt;br /&gt;
produces itel dates to reconcile against, where tecno has no second source at all.&lt;br /&gt;
That check has passed:&lt;br /&gt;
&lt;br /&gt;
  - a 50-imei read-only trial answered 50/50, no errors, no sign rejections, and&lt;br /&gt;
    agreed with our catalog brand on all 50&lt;br /&gt;
  - 18 of the 50 carried a date, and every one of those 18 fell AFTER our own&lt;br /&gt;
    billing date (3 to 1,111 days, median ~220)&lt;br /&gt;
  - the first live tick after deploy returned the same rate: 16 dates, 32 not-yet-&lt;br /&gt;
    activated, 1 malformed response absorbed by the has(&quot;status&quot;) guard&lt;br /&gt;
&lt;br /&gt;
Tecno is also the reason this class exists. Itel was already served by the DCR pull;&lt;br /&gt;
tecno has been served by nothing since 2024-11-20 and has been manual CSV ever since.&lt;br /&gt;
&lt;br /&gt;
Capacity: ~10,400 itel + ~1,890 tecno pending against 14,400 lookups a day. The&lt;br /&gt;
round-robin splits a tick four ways only while all four queues have work; tecno is&lt;br /&gt;
much the smaller pool, so it is exhausted a few hours in, its queues then come back&lt;br /&gt;
empty and itel gets the full 50 again for the rest of the day. Itel still clears&lt;br /&gt;
daily -- roughly 1,875 + 10,650 = 12,525 itel lookups against a 10,401 pool.&lt;/div&gt;~ /trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/scheduled/CarlcareImeiActivationService.java&lt;br /&gt;</description><link>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37485</link><guid>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37485</guid></item>
<item><pubDate>Mon, 31 Aug 2026 18:17:00 +0530</pubDate><dc:creator>amit</dc:creator><title>Rev 37484 – cron metrics: register the status gauge as -1, not 0 ...</title><description>&lt;div&gt;&lt;strong&gt;amit – 1 file(s) modified&lt;/strong&gt;&lt;br/&gt;cron metrics: register the status gauge as -1, not 0&lt;br /&gt;
&lt;br /&gt;
The gauge was created when a job STARTS, and 0 means failure, so any job still&lt;br /&gt;
running its first execution after a restart reported FAILED. Selenium-driven and&lt;br /&gt;
report jobs take minutes, so this is not a narrow window -- it put ten jobs on&lt;br /&gt;
the dashboard as failed after the 2026-08-31 restart when none had failed.&lt;br /&gt;
&lt;br /&gt;
-1 means &apos;has not finished a run yet&apos;. CronJobFailing matches == 0, so those&lt;br /&gt;
jobs are simply absent from the alert until they genuinely complete once.&lt;/div&gt;~ /trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/monitored/CronJobMonitorAspect.java&lt;br /&gt;</description><link>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37484</link><guid>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37484</guid></item>
<item><pubDate>Mon, 31 Aug 2026 18:11:23 +0530</pubDate><dc:creator>amit</dc:creator><title>Rev 37483 – mail: migrate mail_outbox SENDGRID rows to RELAY (applied to production) ...</title><description>&lt;div&gt;&lt;strong&gt;amit – 2 file(s) modified&lt;/strong&gt;&lt;br/&gt;mail: migrate mail_outbox SENDGRID rows to RELAY (applied to production)&lt;br /&gt;
&lt;br /&gt;
SENDGRID never meant SendGrid -- resolveSender() already routed it to the&lt;br /&gt;
Workspace relay, which is why outbox mail kept being delivered while direct&lt;br /&gt;
JavaMailSender injections failed against the real, dead SendGrid bean. So this&lt;br /&gt;
relabels rows without changing where anything is sent.&lt;br /&gt;
&lt;br /&gt;
Applied to hadb1 2026-08-31 18:10 IST:&lt;br /&gt;
  before  SENDGRID=5956  RELAY=0     GOOGLE=4896  (total 10852)&lt;br /&gt;
  after   SENDGRID=0     RELAY=5956  GOOGLE=4896  (total 10852)&lt;br /&gt;
  backup  dtr.mail_outbox_sendgrid_backup_20260831_1810 (5956 rows)&lt;br /&gt;
&lt;br /&gt;
The one PENDING row (id=26838) migrated cleanly and still routes to the relay.&lt;br /&gt;
Only GOOGLE and RELAY remain in the column. Rollback statement is in the script.&lt;br /&gt;
&lt;br /&gt;
The comment claiming legacy SENDGRID rows exist is now false and has been&lt;br /&gt;
corrected; SendGrid appears nowhere in the codebase.&lt;/div&gt;~ /trunk/profitmandi-dao/src/main/java/com/spice/profitmandi/service/mail/MailOutboxService.java&lt;br /&gt;+ /trunk/profitmandi-dao/src/main/resources/sql/migration_mail_outbox_sendgrid_to_relay.sql&lt;br /&gt;</description><link>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37483</link><guid>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37483</guid></item>
<item><pubDate>Mon, 31 Aug 2026 17:59:36 +0530</pubDate><dc:creator>amit</dc:creator><title>Rev 37482 – carlcare: recover tecno/itel activation dates, and fix trunk broken by ...</title><description>&lt;div&gt;&lt;strong&gt;amit – 4 file(s) modified&lt;/strong&gt;&lt;br/&gt;carlcare: recover tecno/itel activation dates, and fix trunk broken by r37479&lt;br /&gt;
&lt;br /&gt;
r37479/r37480 swept a working-copy edit to Application.java into the mail commit: trunk&lt;br /&gt;
has called scheduledTasks.checkCarlcareImeiActivation() since then without containing it,&lt;br /&gt;
so profitmandi-cron has not compiled. This adds the rest.&lt;br /&gt;
&lt;br /&gt;
WHAT THIS IS&lt;br /&gt;
&lt;br /&gt;
The tecno feed has been dead since 2024-11-20, when transsion decommissioned the SAP&lt;br /&gt;
OData hosts cms.tecno-mobile.com:8099 / cms.itel-mobile.com:8099. The imwav DCR portal&lt;br /&gt;
recovers itel but has nothing for tecno -- all three tecno logins together expose 17&lt;br /&gt;
imeis over five years, against 31,928 tecno rows in fofo.activated_imei. Everything since&lt;br /&gt;
has been manual CSV via /imei/upload.&lt;br /&gt;
&lt;br /&gt;
Carlcare is transsion&apos;s own after-sales arm, and the warranty-check page on carlcare.in is&lt;br /&gt;
backed by a public per-imei endpoint that answers for tecno, itel and infinix with no&lt;br /&gt;
login, no cookie and no captcha:&lt;br /&gt;
&lt;br /&gt;
  GET ind-mis-carl.shalltry.com/CarlcareClient/electronic-card/check-extended_warranty-web?imei=&lt;br /&gt;
  sign: md5(SALT + imei)&lt;br /&gt;
&lt;br /&gt;
The sign header is mandatory (without it: code 10022 &quot;Sorry, web sign is error&quot;). The salt&lt;br /&gt;
is in the site&apos;s own bundle, _nuxt/085537c.js module 688, along with the base url; re-read&lt;br /&gt;
that bundle if it ever stops working. status 3 = activated and carries activeTime, status&lt;br /&gt;
2 = device known but not activated yet and activeTime is null.&lt;br /&gt;
&lt;br /&gt;
It is also the semantically right source. The DCR portal serves an INVENTORY report and&lt;br /&gt;
the old SAP feed served a TERTIARY SALES report, whereas activeTime is the date the&lt;br /&gt;
handset was actually activated -- which is what fofo.activated_imei is meant to hold and&lt;br /&gt;
what tertiary payout is computed on.&lt;br /&gt;
&lt;br /&gt;
Measured against hadb1 before writing any of this: tecno 355463920708766 -&gt; 2026-08-28 and&lt;br /&gt;
itel 359207322028000 -&gt; 2026-08-30, both exact matches to rows we already had. A read-only&lt;br /&gt;
trial of 50 itel imeis answered 50/50 with no errors and no sign rejections, agreed with&lt;br /&gt;
our catalog brand on all 50, and returned a date for 18 -- every one of those 18 falling&lt;br /&gt;
AFTER our own billing date, 3 to 1,111 days, median ~220.&lt;br /&gt;
&lt;br /&gt;
SHAPE&lt;br /&gt;
&lt;br /&gt;
Pool queries and saveActivation semantics are the vivo ones, so the two read alike, and no&lt;br /&gt;
DAO change was needed: the pending queries are already brand-generic. The far end is far&lt;br /&gt;
cheaper than vivo&apos;s, one signed GET per imei, so there is no captcha service, no cookie&lt;br /&gt;
store, no session seeding and no verdict reporting.&lt;br /&gt;
&lt;br /&gt;
50 imeis every 5 minutes = 14,400 lookups a day against ~10,400 pending itel, so the whole&lt;br /&gt;
pool is covered daily with headroom. The pool reaches back to 2021, so the ticks are&lt;br /&gt;
themselves the backfill of the nov-2024 blackout; there is no one-off to run.&lt;br /&gt;
&lt;br /&gt;
Two things worth knowing before changing it:&lt;br /&gt;
&lt;br /&gt;
- The batch is drawn ROUND-ROBIN across each (brand, channel) queue, not by concatenating&lt;br /&gt;
  them. A full daily pass can concatenate freely because it walks to the end, but a&lt;br /&gt;
  50-at-a-time tick cannot: itel is ~1,550 secondary against ~8,851 tertiary, so the head&lt;br /&gt;
  of a concatenated list is ~31 straight ticks of pure secondary before one tertiary imei&lt;br /&gt;
  is asked about. The first trial batch was 100% secondary for exactly that reason.&lt;br /&gt;
&lt;br /&gt;
- Every outcome stamps the row, failures included. This is the one deliberate departure&lt;br /&gt;
  from vivo, which leaves a failure unrecorded so it retries next pass -- safe there&lt;br /&gt;
  because the next pass is tomorrow. On a 5-minute cadence it is not: an unstamped imei is&lt;br /&gt;
  due again in five minutes, the query keeps handing back the same 50 rows, the batch never&lt;br /&gt;
  advances past them, and the endpoint is asked the same questions twelve times an hour for&lt;br /&gt;
  as long as it keeps failing. See the runaway documented on oppoRealmeImeiActivation.&lt;br /&gt;
&lt;br /&gt;
BRANDS is itel alone to start. That is a rollout order, not a limit of the endpoint: itel&lt;br /&gt;
is the brand we can CHECK, because the DCR pull independently produces itel dates to&lt;br /&gt;
reconcile against, where tecno has nothing. Add &quot;Tecno&quot; once a day&apos;s rows agree. Both feeds&lt;br /&gt;
may write itel meanwhile with no coordination -- the pool query only returns imeis whose&lt;br /&gt;
activationTimestamp is still null, so whatever one fills has left the other&apos;s pool.&lt;br /&gt;
&lt;br /&gt;
Scheduled tick plus a --checkCarlcareImeiActivation flag, both wired.&lt;/div&gt;+ /trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/scheduled/CarlcareImeiActivationService.java&lt;br /&gt;~ /trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/scheduled/ScheduledSkeleton.java&lt;br /&gt;~ /trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/scheduled/ScheduledTasks.java&lt;br /&gt;~ /trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/scheduled/StandAlone.java&lt;br /&gt;</description><link>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37482</link><guid>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37482</guid></item>
<item><pubDate>Mon, 31 Aug 2026 17:59:05 +0530</pubDate><dc:creator>amit</dc:creator><title>Rev 37481 – imei activation: one 2-day billing floor for every brand, and ...</title><description>&lt;div&gt;&lt;strong&gt;amit – 3 file(s) modified&lt;/strong&gt;&lt;br/&gt;imei activation: one 2-day billing floor for every brand, and drop two dead methods&lt;br /&gt;
&lt;br /&gt;
The pending pool is a UNION of two channels, not an intersection: an imei qualifies&lt;br /&gt;
if WE billed it to the partner (selectImeiActivationByBrand) or the PARTNER billed it&lt;br /&gt;
on to the end customer (selectImeiActivationByBrandTertiary). The two are disjoint by&lt;br /&gt;
construction -- the secondary query excludes anything carrying a FofoLineItem -- so a&lt;br /&gt;
unit moves from one to the other as it sells through and is never asked about twice.&lt;br /&gt;
Said so on the interface, since the pairing was only documented at the call sites.&lt;br /&gt;
&lt;br /&gt;
billedBefore moves from now-1d to now-2d, so both channels ask only about stock billed&lt;br /&gt;
MORE THAN 2 DAYS ago. Anything sold in the last 48 hours has essentially never been&lt;br /&gt;
activated yet, so the lookup is spent for nothing; it is not lost, the same imei comes&lt;br /&gt;
back into the pool as soon as it crosses the floor, and again every day after that&lt;br /&gt;
until it activates. Kept in the repository rather than per caller so vivo, oppo, realme,&lt;br /&gt;
motorola and the new carlcare pass inherit one rule instead of drifting apart.&lt;br /&gt;
&lt;br /&gt;
Costs almost nothing today -- secondary pool, old floor vs new:&lt;br /&gt;
&lt;br /&gt;
  itel 1550 -&gt; 1550    realme    849 -&gt;  849    oppo     2021 -&gt; 2019&lt;br /&gt;
  vivo 5018 -&gt; 5016    motorola 1168 -&gt; 1153    tecno     502 -&gt;  495&lt;br /&gt;
&lt;br /&gt;
26 rows across six brands, each deferred by one day.&lt;br /&gt;
&lt;br /&gt;
Removed as dead:&lt;br /&gt;
&lt;br /&gt;
  selectImeiActivationPendingByRealme -- a verbatim duplicate of&lt;br /&gt;
  selectImeiActivationPendingByBrand down to the named query and the parameters. Nothing&lt;br /&gt;
  called it; StandAlone already used the brand-generic method for realme and said so in&lt;br /&gt;
  a comment, which is now updated.&lt;br /&gt;
&lt;br /&gt;
  selectImeiSoldNotActivatedByBrand -- interface, impl and named query. No callers&lt;br /&gt;
  anywhere in web, fofo, cron or dao.&lt;/div&gt;~ /trunk/profitmandi-dao/src/main/java/com/spice/profitmandi/dao/entity/fofo/ActivatedImei.java&lt;br /&gt;~ /trunk/profitmandi-dao/src/main/java/com/spice/profitmandi/dao/repository/fofo/ActivatedImeiRepository.java&lt;br /&gt;~ /trunk/profitmandi-dao/src/main/java/com/spice/profitmandi/dao/repository/fofo/ActivatedImeiRepositoryImpl.java&lt;br /&gt;</description><link>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37481</link><guid>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37481</guid></item>
<item><pubDate>Mon, 31 Aug 2026 17:55:16 +0530</pubDate><dc:creator>amit</dc:creator><title>Rev 37480 – mail: correct a wrong claim in r37479 -- the Google ...</title><description>&lt;div&gt;&lt;strong&gt;amit – 3 file(s) modified&lt;/strong&gt;&lt;br/&gt;mail: correct a wrong claim in r37479 -- the Google app password IS valid&lt;br /&gt;
&lt;br /&gt;
r37479 stated googleMailSender&apos;s app password was rejected. That was wrong. The&lt;br /&gt;
test behind it resolved smtp.gmail.com over IPv6; repeating it over IPv4 with&lt;br /&gt;
the same credential gives AUTH OK on both 465 and 587.&lt;br /&gt;
&lt;br /&gt;
The real fault is not the credential and not the bean config, both of which are&lt;br /&gt;
correct. SMTP from this host works over IPv4 only:&lt;br /&gt;
&lt;br /&gt;
  smtp.gmail.com   IPv4 -&gt; AUTH OK        IPv6 -&gt; 535 5.7.8 Username and Password not accepted&lt;br /&gt;
  smtp-relay       IPv4 -&gt; 250 MAIL FROM  IPv6 -&gt; 550 5.7.1 Invalid credentials for relay&lt;br /&gt;
&lt;br /&gt;
The JVM prefers IPv4, which is the only reason mail leaves this box at all.&lt;br /&gt;
Anything that prefers IPv6 fails on both paths.&lt;/div&gt;~ /trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/Application.java&lt;br /&gt;~ /trunk/profitmandi-fofo/src/main/java/com/spice/profitmandi/web/config/AppConfig.java&lt;br /&gt;~ /trunk/profitmandi-web/src/main/java/com/spice/profitmandi/web/config/AppConfig.java&lt;br /&gt;</description><link>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37480</link><guid>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37480</guid></item>
<item><pubDate>Mon, 31 Aug 2026 17:49:38 +0530</pubDate><dc:creator>amit</dc:creator><title>Rev 37479 – mail: make the relay the default sender, not the Google ...</title><description>&lt;div&gt;&lt;strong&gt;amit – 3 file(s) modified&lt;/strong&gt;&lt;br/&gt;mail: make the relay the default sender, not the Google identity&lt;br /&gt;
&lt;br /&gt;
Correcting r37474-37476. Those made googleMailSender @Primary on the assumption&lt;br /&gt;
its credentials worked. Tested against the live servers from the prod host:&lt;br /&gt;
&lt;br /&gt;
  googleMailSender  535 5.7.8 Username and Password not accepted (BadCredentials)&lt;br /&gt;
                    -- the app password in the source is no longer valid&lt;br /&gt;
  relay over IPv4   250 OK for MAIL FROM:&amp;lt;&lt;a href=&quot;mailto:noreply@smartdukaan.com&quot;&gt;noreply@smartdukaan.com&lt;/a&gt;&gt;&lt;br /&gt;
  relay over IPv6   550 5.7.1 Invalid credentials for relay&lt;br /&gt;
&lt;br /&gt;
So promoting google would have replaced one broken default with another. The&lt;br /&gt;
relay is what actually delivers today and it becomes &apos;mailSender&apos;. It does not&lt;br /&gt;
authenticate -- Workspace authorises it by allowlisted source IP -- so sending&lt;br /&gt;
as noreply@ is legitimate there and AuthenticatedIdentityMailSender correctly&lt;br /&gt;
leaves it alone.&lt;br /&gt;
&lt;br /&gt;
googleMailSender stays available by qualifier. Point the default back at it once&lt;br /&gt;
a valid app password is issued for &lt;a href=&quot;mailto:sdtech@smartdukaan.com&quot;&gt;sdtech@smartdukaan.com&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
Also noted in the javadoc: the relay allowlist covers the IPv4 address only, so&lt;br /&gt;
anything that prefers IPv6 will be refused.&lt;/div&gt;~ /trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/Application.java&lt;br /&gt;~ /trunk/profitmandi-fofo/src/main/java/com/spice/profitmandi/web/config/AppConfig.java&lt;br /&gt;~ /trunk/profitmandi-web/src/main/java/com/spice/profitmandi/web/config/AppConfig.java&lt;br /&gt;</description><link>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37479</link><guid>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37479</guid></item>
<item><pubDate>Mon, 31 Aug 2026 17:38:59 +0530</pubDate><dc:creator>amit</dc:creator><title>Rev 37478 – mail: MailOutboxService implements MailQueue  Adds the single entry point, ...</title><description>&lt;div&gt;&lt;strong&gt;amit – 1 file(s) modified&lt;/strong&gt;&lt;br/&gt;mail: MailOutboxService implements MailQueue&lt;br /&gt;
&lt;br /&gt;
Adds the single entry point, queue(MailRequest). The twenty-odd queueMail*&lt;br /&gt;
overloads remain as delegates because ~112 call sites use them, but they now&lt;br /&gt;
funnel through one path rather than each building their own argument list.&lt;br /&gt;
&lt;br /&gt;
Selecting the transport moves here too: MailSenderType.GOOGLE maps to the&lt;br /&gt;
authenticated sender, anything else to the relay.&lt;/div&gt;~ /trunk/profitmandi-dao/src/main/java/com/spice/profitmandi/service/mail/MailOutboxService.java&lt;br /&gt;</description><link>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37478</link><guid>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37478</guid></item>
<item><pubDate>Mon, 31 Aug 2026 17:38:48 +0530</pubDate><dc:creator>amit</dc:creator><title>Rev 37477 – mail: one way to send mail, and nothing sends inline ...</title><description>&lt;div&gt;&lt;strong&gt;amit – 8 file(s) modified&lt;/strong&gt;&lt;br/&gt;mail: one way to send mail, and nothing sends inline&lt;br /&gt;
&lt;br /&gt;
There were three overlapping APIs -- Utils.sendMail* (7 static overloads),&lt;br /&gt;
EmailServiceImpl.sendMail* (7 more) and MailOutboxService.queueMail* (20). The&lt;br /&gt;
first two built their own MimeMessage, hardcoded From: &lt;a href=&quot;mailto:noreply@smartdukaan.com&quot;&gt;noreply@smartdukaan.com&lt;/a&gt;&lt;br /&gt;
in eleven places, and pushed straight down whichever JavaMailSender the caller&lt;br /&gt;
passed.&lt;br /&gt;
&lt;br /&gt;
That From is why mail was refused: an authenticated Workspace session may send&lt;br /&gt;
only as the account it logged in as, and noreply@ is a different identity in the&lt;br /&gt;
same domain. Sending inline also meant a failed send was lost outright and took&lt;br /&gt;
the calling job down with it, which is how one expired credential came to mark&lt;br /&gt;
ten report jobs FAILED.&lt;br /&gt;
&lt;br /&gt;
New in profitmandi-common:&lt;br /&gt;
  MailQueue      - the single entry point; queue() returns once the mail is&lt;br /&gt;
                   recorded, not once it is sent&lt;br /&gt;
  MailRequest    - one value object replacing the 34 overloads&lt;br /&gt;
  MailSenderType - GOOGLE (authenticated, From rewritten) or RELAY (IP-authorised)&lt;br /&gt;
&lt;br /&gt;
The interface lives in common while MailOutboxService implements it in dao,&lt;br /&gt;
because dao depends on common and not the reverse -- Utils and EmailService&lt;br /&gt;
could not otherwise reach the outbox at all.&lt;br /&gt;
&lt;br /&gt;
Utils and EmailService keep their signatures so the ~38 call sites still&lt;br /&gt;
compile, but now delegate and ignore the JavaMailSender argument; choosing a&lt;br /&gt;
transport was never the caller&apos;s business. Both are marked deprecated.&lt;br /&gt;
MailQueueHolder bridges the static helpers to the bean and is documented as a&lt;br /&gt;
compromise, not a pattern.&lt;/div&gt;+ /trunk/profitmandi-common/src/main/java/com/spice/profitmandi/common/mail&lt;br /&gt;+ /trunk/profitmandi-common/src/main/java/com/spice/profitmandi/common/mail/MailQueue.java&lt;br /&gt;+ /trunk/profitmandi-common/src/main/java/com/spice/profitmandi/common/mail/MailQueueHolder.java&lt;br /&gt;+ /trunk/profitmandi-common/src/main/java/com/spice/profitmandi/common/mail/MailRequest.java&lt;br /&gt;+ /trunk/profitmandi-common/src/main/java/com/spice/profitmandi/common/mail/MailSenderType.java&lt;br /&gt;~ /trunk/profitmandi-common/src/main/java/com/spice/profitmandi/common/services/EmailService.java&lt;br /&gt;~ /trunk/profitmandi-common/src/main/java/com/spice/profitmandi/common/services/EmailServiceImpl.java&lt;br /&gt;~ /trunk/profitmandi-common/src/main/java/com/spice/profitmandi/common/util/Utils.java&lt;br /&gt;</description><link>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37477</link><guid>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37477</guid></item>
<item><pubDate>Mon, 31 Aug 2026 17:19:03 +0530</pubDate><dc:creator>amit</dc:creator><title>Rev 37476 – mail: delete the dead SendGrid bean from fofo, make the ...</title><description>&lt;div&gt;&lt;strong&gt;amit – 1 file(s) modified&lt;/strong&gt;&lt;br/&gt;mail: delete the dead SendGrid bean from fofo, make the authenticated identity primary&lt;br /&gt;
&lt;br /&gt;
googleMailSender is now @Primary and answers to &apos;mailSender&apos;. Two senders remain:&lt;br /&gt;
the authenticated Workspace identity and the IP-authorised relay.&lt;/div&gt;~ /trunk/profitmandi-fofo/src/main/java/com/spice/profitmandi/web/config/AppConfig.java&lt;br /&gt;</description><link>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37476</link><guid>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37476</guid></item>
<item><pubDate>Mon, 31 Aug 2026 17:11:39 +0530</pubDate><dc:creator>amit</dc:creator><title>Rev 37475 – mail: delete the dead SendGrid bean from web, make the ...</title><description>&lt;div&gt;&lt;strong&gt;amit – 1 file(s) modified&lt;/strong&gt;&lt;br/&gt;mail: delete the dead SendGrid bean from web, make the authenticated identity primary&lt;br /&gt;
&lt;br /&gt;
Removes the live SendGrid bean plus two commented-out corpses (an old&lt;br /&gt;
&lt;a href=&quot;mailto:build@shop2020.in&quot;&gt;build@shop2020.in&lt;/a&gt; sender and a dead alias). googleMailSender is now @Primary and&lt;br /&gt;
answers to &apos;mailSender&apos;, so unqualified injections get the working authenticated&lt;br /&gt;
sender instead of one that rejects with 535.&lt;/div&gt;~ /trunk/profitmandi-web/src/main/java/com/spice/profitmandi/web/config/AppConfig.java&lt;br /&gt;</description><link>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37475</link><guid>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37475</guid></item>
<item><pubDate>Mon, 31 Aug 2026 17:11:26 +0530</pubDate><dc:creator>amit</dc:creator><title>Rev 37474 – mail: delete the dead SendGrid bean from cron, make the ...</title><description>&lt;div&gt;&lt;strong&gt;amit – 2 file(s) modified&lt;/strong&gt;&lt;br/&gt;mail: delete the dead SendGrid bean from cron, make the authenticated identity primary&lt;br /&gt;
&lt;br /&gt;
The @Primary bean named &apos;mailSender&apos; was SendGrid, so every unqualified&lt;br /&gt;
JavaMailSender injection got it -- and SendGrid rejects with 535, which is what&lt;br /&gt;
was failing 10 report/notification cron jobs (dailyTrackingReport,&lt;br /&gt;
monthlyTargetForPartner, sendFeebackSalesAndRbm and others). 420 occurrences in&lt;br /&gt;
one log.&lt;br /&gt;
&lt;br /&gt;
googleMailSender now answers to both &apos;googleMailSender&apos; and &apos;mailSender&apos; and is&lt;br /&gt;
@Primary, so those injections resolve to a sender that works and whose From is&lt;br /&gt;
rewritten to the authenticated identity. No call site changes.&lt;/div&gt;~ /trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/Application.java&lt;br /&gt;~ /trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/scheduled/MailOutboxScheduler.java&lt;br /&gt;</description><link>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37474</link><guid>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37474</guid></item>
<item><pubDate>Mon, 31 Aug 2026 17:11:25 +0530</pubDate><dc:creator>amit</dc:creator><title>Rev 37473 – mail: retire the SENDGRID sender label  SendGrid was never ...</title><description>&lt;div&gt;&lt;strong&gt;amit – 1 file(s) modified&lt;/strong&gt;&lt;br/&gt;mail: retire the SENDGRID sender label&lt;br /&gt;
&lt;br /&gt;
SendGrid was never actually used by the outbox -- resolveSender() already fell&lt;br /&gt;
through to the Workspace relay for it, which is why outbox mail kept working&lt;br /&gt;
while direct JavaMailSender injections failed.&lt;br /&gt;
&lt;br /&gt;
Two senders remain, each named for what it is:&lt;br /&gt;
  GOOGLE - authenticates as &lt;a href=&quot;mailto:sdtech@smartdukaan.com&quot;&gt;sdtech@smartdukaan.com&lt;/a&gt;; AuthenticatedIdentityMailSender&lt;br /&gt;
           rewrites every From to that identity, because Workspace binds an&lt;br /&gt;
           authenticated session to one address and rejects any other with&lt;br /&gt;
           &apos;535 Authorization failed: Authenticated user is not authorized to send mail&apos;&lt;br /&gt;
  RELAY  - Workspace relay, no auth, authorised by source IP, may send as noreply@&lt;br /&gt;
&lt;br /&gt;
Default convenience methods now route to RELAY explicitly rather than through a&lt;br /&gt;
label that named a provider we do not use.&lt;/div&gt;~ /trunk/profitmandi-dao/src/main/java/com/spice/profitmandi/service/mail/MailOutboxService.java&lt;br /&gt;</description><link>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37473</link><guid>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37473</guid></item>
<item><pubDate>Mon, 31 Aug 2026 16:08:03 +0530</pubDate><dc:creator>amit</dc:creator><title>Rev 37472 – IMEI activation: one snapshotted daily pass per brand, on one ...</title><description>&lt;div&gt;&lt;strong&gt;amit – 12 file(s) modified&lt;/strong&gt;&lt;br/&gt;IMEI activation: one snapshotted daily pass per brand, on one thread, with per-brand metrics&lt;br /&gt;
&lt;br /&gt;
Four @Scheduled jobs every 5 minutes become two daily passes. Oppo and realme&lt;br /&gt;
share one thread and alternate in 25-imei chunks, so exactly one ChromeDriver is&lt;br /&gt;
alive at a time instead of four; vivo keeps its own thread since it is direct&lt;br /&gt;
HTTP and does not contend for a browser.&lt;br /&gt;
&lt;br /&gt;
The pass snapshots its pool before any browser starts and walks that list to the&lt;br /&gt;
end. It never re-queries, and that is the actual fix. A failed lookup never&lt;br /&gt;
reaches dateMap.put, so no row is written, so createTimestamp is not bumped, so&lt;br /&gt;
the imei was eligible again on the next tick five minutes later. Measured 29-Aug:&lt;br /&gt;
realme issued 4,524 requests against 1,004 distinct imeis -- 4.5 asks each, 78%&lt;br /&gt;
of the day&apos;s budget spent re-asking -- while oppo, which rarely fails, sat at&lt;br /&gt;
1.03. More requests hardened the block, which caused more failures. A pass bounds&lt;br /&gt;
that: a failure costs one retry tomorrow, never one in five minutes.&lt;br /&gt;
&lt;br /&gt;
This supersedes the r37447/r37448/r37449 argument about driver count, which was&lt;br /&gt;
about the wrong variable. That argument blamed realme&apos;s collapse on CPU&lt;br /&gt;
contention pushing the captcha render past the element waits. The logs do not&lt;br /&gt;
support it: on 29-Aug oppo took ZERO canvas timeouts across all 24 hours on the&lt;br /&gt;
same box, same six cores, same driver count, same captcha vendor, load average&lt;br /&gt;
0.9 -- including the 15:00-23:00 window in which realme solved nothing at all.&lt;br /&gt;
Realme&apos;s own canvas wait is 15s against oppo&apos;s 8s, so the longer wait is the one&lt;br /&gt;
expiring. What realme&apos;s timeout rate tracks is its own daily request volume, and&lt;br /&gt;
it resets at midnight: 920/day -&gt; 0.3%, 3,467/day -&gt; 28%, 4,524/day -&gt; 75%. That&lt;br /&gt;
is realme.com declining to serve the widget.&lt;br /&gt;
&lt;br /&gt;
DAYS=0 is deliberate and is not an off-by-one: the pool filter is&lt;br /&gt;
createTimestamp &amp;lt; now().atStartOfDay().minusDays(DAYS), so DAYS=1 measures&lt;br /&gt;
against yesterday midnight and silently yields a two-day cadence, which is what&lt;br /&gt;
oppo and realme were running.&lt;br /&gt;
&lt;br /&gt;
Sizing measured on prod for a midnight start: oppo 4,133 and realme 2,118 imeis,&lt;br /&gt;
11.7h + 8.4h = 20.1 hours of a single thread. It fits with no slack; if the&lt;br /&gt;
&apos;pass finished&apos; counts come in short of &apos;pass starting&apos;, the lever is DAYS=1&lt;br /&gt;
rather than a second thread.&lt;br /&gt;
&lt;br /&gt;
Observability: ImeiActivationGauges publishes the funnel per brand on&lt;br /&gt;
/actuator/prometheus, which alloy already scrapes on this host -- due, churned,&lt;br /&gt;
captcha_shown, captcha_solved, answered, dates_found, errors, run_seconds and&lt;br /&gt;
last_finish_epoch. Each stage fails differently and says what broke. Rates are&lt;br /&gt;
left to PromQL. The stage that matters for health is answered: churned&gt;0 with&lt;br /&gt;
answered==0 is precisely the shape of both silent outages this year (oppo wrote&lt;br /&gt;
nothing for a week; the vivo captcha solver was dead for 46 days). dates_found is&lt;br /&gt;
deliberately NOT a health signal -- when the multi-year backlog drained at the&lt;br /&gt;
end of August, yield fell from ~100% to 2-3% on the same day across all three&lt;br /&gt;
brands with nothing broken.&lt;br /&gt;
&lt;br /&gt;
Nagios cleanup: the Nagios server and every NRPE daemon are gone, so&lt;br /&gt;
WriteToPropertiesFile and the commented-out blocks that fed&lt;br /&gt;
nagios-cron.properties are deleted, and NagiosMonitorTasks is renamed&lt;br /&gt;
BalanceMonitorTasks for the transport it actually uses. Noted there that nothing&lt;br /&gt;
calls it -- there is no @Scheduled entry and no other caller -- which is why both&lt;br /&gt;
balance gauges have always read -1.&lt;/div&gt;~ /trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/Application.java&lt;br /&gt;~ /trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/monitored/BalanceGauges.java&lt;br /&gt;+ /trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/monitored/BalanceMonitorTasks.java &lt;i&gt;(copied from /trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/monitored/NagiosMonitorTasks.java@37471)&lt;/i&gt;&lt;br /&gt;+ /trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/monitored/ImeiActivationGauges.java&lt;br /&gt;x /trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/monitored/NagiosMonitorTasks.java&lt;br /&gt;x /trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/properties/WriteToPropertiesFile.java&lt;br /&gt;~ /trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/scheduled/CheckOppoWarrantyTask.java&lt;br /&gt;~ /trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/scheduled/CheckRealmeWarrantyTask.java&lt;br /&gt;~ /trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/scheduled/ScheduledSkeleton.java&lt;br /&gt;~ /trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/scheduled/ScheduledTasks.java&lt;br /&gt;~ /trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/scheduled/StandAlone.java&lt;br /&gt;~ /trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/scheduled/VivoImeiActivationService.java&lt;br /&gt;</description><link>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37472</link><guid>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37472</guid></item>
<item><pubDate>Mon, 31 Aug 2026 16:07:40 +0530</pubDate><dc:creator>amit</dc:creator><title>Rev 37471 – IMEI activation pools: skip stock billed today or yesterday  ...</title><description>&lt;div&gt;&lt;strong&gt;amit – 2 file(s) modified&lt;/strong&gt;&lt;br/&gt;IMEI activation pools: skip stock billed today or yesterday&lt;br /&gt;
&lt;br /&gt;
Both pool named queries now exclude serials whose sale is inside the last 48&lt;br /&gt;
hours -- o.billingTimestamp for the secondary path, fo.createTimestamp for the&lt;br /&gt;
tertiary one. A handset sold in the last two days has essentially never been&lt;br /&gt;
activated yet, so the lookup is spent for nothing: measured on prod, the&lt;br /&gt;
sold-within-180-days cohort returns an activation date on 2.4% (vivo) to 8.4%&lt;br /&gt;
(oppo) of lookups, against 43-97% for stock sold over a year ago.&lt;br /&gt;
&lt;br /&gt;
billedBefore is set in ActivatedImeiRepositoryImpl rather than passed by the&lt;br /&gt;
caller, so no method signature changes and every brand inherits it -- vivo,&lt;br /&gt;
oppo, realme and motorola all draw from these two queries and nothing outside&lt;br /&gt;
profitmandi-cron uses them.&lt;br /&gt;
&lt;br /&gt;
This is correctness rather than capacity: it trims 30 imeis from the oppo pool&lt;br /&gt;
and 9 from realme.&lt;/div&gt;~ /trunk/profitmandi-dao/src/main/java/com/spice/profitmandi/dao/entity/fofo/ActivatedImei.java&lt;br /&gt;~ /trunk/profitmandi-dao/src/main/java/com/spice/profitmandi/dao/repository/fofo/ActivatedImeiRepositoryImpl.java&lt;br /&gt;</description><link>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37471</link><guid>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37471</guid></item>
<item><pubDate>Mon, 31 Aug 2026 15:50:16 +0530</pubDate><dc:creator>amit</dc:creator><title>Rev 37470 – Remove dead Itel/Tecno SAP activation services  Both services call ...</title><description>&lt;div&gt;&lt;strong&gt;amit – 4 file(s) modified&lt;/strong&gt;&lt;br/&gt;Remove dead Itel/Tecno SAP activation services&lt;br /&gt;
&lt;br /&gt;
Both services call SAP OData endpoints that Transsion decommissioned on 2024-11-20:&lt;br /&gt;
&lt;br /&gt;
  ItelImeiActivationService  -&gt; cms.itel-mobile.com:8099/.../ZTERTIARY_SALES_REPRT_SRV&lt;br /&gt;
  TecnoImeiActivation        -&gt; cms.tecno-mobile.com:8099/.../ZTERTIARY_SALES_REPRT_SRV&lt;br /&gt;
&lt;br /&gt;
Both hosts now refuse connections outright (verified from the prod app server).&lt;br /&gt;
That decommissioning is what stopped Itel and Tecno activation ingest within the&lt;br /&gt;
same hour on 2024-11-20; everything since has arrived via manual CSV upload.&lt;br /&gt;
Neither service has produced a row in 21 months and neither can again.&lt;br /&gt;
&lt;br /&gt;
Removed:&lt;br /&gt;
  - both service classes&lt;br /&gt;
  - ScheduledTasks.checkItelImeiActivation / .checkTecnoImeiActivation, their&lt;br /&gt;
    @Autowired fields and imports (the only callers)&lt;br /&gt;
  - the --checkItelImeiActivation / --checkTecnoImeiActivation startup args&lt;br /&gt;
&lt;br /&gt;
Unaffected: ItelImeiActivationNewService and checkItelImeiActivationNew, which&lt;br /&gt;
target the current imwav portal and were fixed in r37467.&lt;/div&gt;~ /trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/Application.java&lt;br /&gt;x /trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/itelImeiActivation/ItelImeiActivationService.java&lt;br /&gt;x /trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/itelImeiActivation/TecnoImeiActivation.java&lt;br /&gt;~ /trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/scheduled/ScheduledTasks.java&lt;br /&gt;</description><link>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37470</link><guid>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37470</guid></item>
<item><pubDate>Mon, 31 Aug 2026 13:40:26 +0530</pubDate><dc:creator>ranu</dc:creator><title>Rev 37469 – rbm drr dashboard , all today po rbm view maped</title><description>&lt;div&gt;&lt;strong&gt;ranu – 1 file(s) modified&lt;/strong&gt;&lt;br/&gt;rbm drr dashboard , all today po rbm view maped&lt;/div&gt;~ /trunk/profitmandi-fofo/src/main/webapp/WEB-INF/views/ftl/rbm-drr-dashboard.vm&lt;br /&gt;</description><link>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37469</link><guid>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37469</guid></item>
<item><pubDate>Mon, 31 Aug 2026 13:14:17 +0530</pubDate><dc:creator>ranu</dc:creator><title>Rev 37468 – rbm drr dashboard new ui looks</title><description>&lt;div&gt;&lt;strong&gt;ranu – 1 file(s) modified&lt;/strong&gt;&lt;br/&gt;rbm drr dashboard new ui looks&lt;/div&gt;~ /trunk/profitmandi-fofo/src/main/webapp/WEB-INF/views/ftl/rbm-drr-dashboard.vm&lt;br /&gt;</description><link>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37468</link><guid>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37468</guid></item>
<item><pubDate>Mon, 31 Aug 2026 11:30:33 +0530</pubDate><dc:creator>amit</dc:creator><title>Rev 37467 – Fix Itel/Tecno activation pull: live session tokens, correct credentials, per-account ...</title><description>&lt;div&gt;&lt;strong&gt;amit – 1 file(s) modified&lt;/strong&gt;&lt;br/&gt;Fix Itel/Tecno activation pull: live session tokens, correct credentials, per-account isolation&lt;br /&gt;
&lt;br /&gt;
The imwav pull has never worked since it replaced the SAP feed. Three defects:&lt;br /&gt;
&lt;br /&gt;
- Login response was discarded and the listing call sent p-auth/utoken values&lt;br /&gt;
  captured by hand in Jul-2023, so every request went out with an expired session.&lt;br /&gt;
  Now takes utoken/urToken from the login response.&lt;br /&gt;
- Itel (IN404338I) carried a stale password (Dukaan@1234), which is why its pull&lt;br /&gt;
  started failing Feb-2026. Corrected, and the two missing Tecno branch accounts&lt;br /&gt;
  (Noida IN411226T, Lucknow IN404339T) added - the code previously knew only&lt;br /&gt;
  IN404338T, which has no enterprise scope and returns nothing.&lt;br /&gt;
- Existing rows were looked up by the activation date string instead of the IMEI.&lt;br /&gt;
  serial_number is the primary key, so the lookup always missed and re-persisted a&lt;br /&gt;
  row that already existed, colliding on duplicate key on any re-pull.&lt;br /&gt;
&lt;br /&gt;
Also pulls each account independently so one dead login cannot abort the remaining&lt;br /&gt;
accounts, and fails loudly when the portal rejects a login or the report call&lt;br /&gt;
instead of proceeding into a confusing downstream error.&lt;/div&gt;~ /trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/itelImeiActivation/ItelImeiActivationNewService.java&lt;br /&gt;</description><link>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37467</link><guid>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37467</guid></item>
<item><pubDate>Mon, 31 Aug 2026 11:10:13 +0530</pubDate><dc:creator>amit</dc:creator><title>Rev 37466 – Show aged Apple stock on the partner Total Investment breakdown ...</title><description>&lt;div&gt;&lt;strong&gt;amit – 1 file(s) modified&lt;/strong&gt;&lt;br/&gt;Show aged Apple stock on the partner Total Investment breakdown&lt;br /&gt;
&lt;br /&gt;
Adds an &apos;Apple Stock More Than 30 Days&apos; row below &apos;Unbilled Activated Stock&apos;&lt;br /&gt;
so the reason a partner&apos;s suggested credit limit dropped is visible. Display&lt;br /&gt;
only -- the figure is not deducted from the other rows.&lt;br /&gt;
&lt;br /&gt;
Value comes from partnerStat.tmp, so it reads 0 until the cron that rebuilds&lt;br /&gt;
that file has run against the new build.&lt;/div&gt;~ /trunk/profitmandi-fofo/src/main/webapp/WEB-INF/views/ftl/partner-investment.vm&lt;br /&gt;</description><link>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37466</link><guid>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37466</guid></item>
<item><pubDate>Mon, 31 Aug 2026 11:10:07 +0530</pubDate><dc:creator>amit</dc:creator><title>Rev 37465 – Exclude aged Apple stock from investment when suggesting partner credit ...</title><description>&lt;div&gt;&lt;strong&gt;amit – 1 file(s) modified&lt;/strong&gt;&lt;br/&gt;Exclude aged Apple stock from investment when suggesting partner credit limits&lt;br /&gt;
&lt;br /&gt;
Apple handsets a partner has held more than 30 days from GRN no longer count&lt;br /&gt;
toward the investment that drives the suggested limit. Applied in&lt;br /&gt;
getSuggestedAmount via getCreditableInvestment, covering the SDDIRECT,&lt;br /&gt;
hundred-percent and SIDBI branches. The aged amount is logged alongside each&lt;br /&gt;
limit change so a drop can be traced.&lt;br /&gt;
&lt;br /&gt;
Scoped to the limit calculation only -- getTotalInvestment() is untouched, so&lt;br /&gt;
checkout payment options, the investment-OK gates and partner-facing screens&lt;br /&gt;
see the same stock value as before.&lt;br /&gt;
&lt;br /&gt;
Measured on prod at time of commit: 580 units / ~3.94 Cr across 88 partners.&lt;/div&gt;~ /trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/scheduled/PartnerLimitHelper.java&lt;br /&gt;</description><link>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37465</link><guid>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37465</guid></item>
<item><pubDate>Mon, 31 Aug 2026 11:10:00 +0530</pubDate><dc:creator>amit</dc:creator><title>Rev 37464 – Track aged Apple stock on partner investment; add category filter ...</title><description>&lt;div&gt;&lt;strong&gt;amit – 5 file(s) modified&lt;/strong&gt;&lt;br/&gt;Track aged Apple stock on partner investment; add category filter to stock value lookup&lt;br /&gt;
&lt;br /&gt;
selectPartnerStockValueMap now takes a nullable categoryId so the same query&lt;br /&gt;
serves both the existing Live Demo exclusion (null = all categories) and the&lt;br /&gt;
new Apple handset lookup (category 10006). Existing callers pass null and are&lt;br /&gt;
behaviourally unchanged.&lt;br /&gt;
&lt;br /&gt;
PartnerDailyInvestment carries agedAppleStockAmount, populated in both the&lt;br /&gt;
single and batch investment paths. It is JPA-@Transient, so no schema change,&lt;br /&gt;
and it still travels through the partnerStat.tmp serialization the FOFO&lt;br /&gt;
investment screen reads. Deliberately not part of getTotalInvestment() -- the&lt;br /&gt;
deduction is a credit-limit policy, so stock value is unchanged for checkout,&lt;br /&gt;
the investment-OK gates and partner-facing screens.&lt;/div&gt;~ /trunk/profitmandi-dao/src/main/java/com/spice/profitmandi/dao/entity/fofo/PartnerDailyInvestment.java&lt;br /&gt;~ /trunk/profitmandi-dao/src/main/java/com/spice/profitmandi/dao/repository/fofo/InventoryItemRepository.java&lt;br /&gt;~ /trunk/profitmandi-dao/src/main/java/com/spice/profitmandi/dao/repository/fofo/InventoryItemRepositoryImpl.java&lt;br /&gt;~ /trunk/profitmandi-dao/src/main/java/com/spice/profitmandi/service/inventory/InventoryServiceImpl.java&lt;br /&gt;~ /trunk/profitmandi-dao/src/main/java/com/spice/profitmandi/service/PartnerInvestmentServiceImpl.java&lt;br /&gt;</description><link>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37464</link><guid>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37464</guid></item>
<item><pubDate>Mon, 31 Aug 2026 11:09:10 +0530</pubDate><dc:creator>amit</dc:creator><title>Rev 37463 – Add aged-stock constants for Apple credit-limit haircut  Apple handsets ...</title><description>&lt;div&gt;&lt;strong&gt;amit – 1 file(s) modified&lt;/strong&gt;&lt;br/&gt;Add aged-stock constants for Apple credit-limit haircut&lt;br /&gt;
&lt;br /&gt;
Apple handsets held by a partner beyond AGED_STOCK_APPLE_DAYS (30) days&lt;br /&gt;
from GRN are excluded from the investment used to suggest a credit limit.&lt;/div&gt;~ /trunk/profitmandi-common/src/main/java/com/spice/profitmandi/common/model/ProfitMandiConstants.java&lt;br /&gt;</description><link>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37463</link><guid>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37463</guid></item>
<item><pubDate>Mon, 31 Aug 2026 11:07:59 +0530</pubDate><dc:creator>amit</dc:creator><title>Rev 37462 – Fire TagListing Solr update after commit instead of inside the ...</title><description>&lt;div&gt;&lt;strong&gt;amit – 1 file(s) modified&lt;/strong&gt;&lt;br/&gt;Fire TagListing Solr update after commit instead of inside the write transaction&lt;br /&gt;
&lt;br /&gt;
TagListingChangeListener used a plain @EventListener, so the async Solr rebuild&lt;br /&gt;
ran on a separate thread in a new transaction while the publishing controller&lt;br /&gt;
transaction was still open. FofoSolr.updateSingleCatalog then re-read&lt;br /&gt;
tag_listing on a connection that could not see the uncommitted rows and&lt;br /&gt;
re-indexed the pre-change price; for a catalog&apos;s first-ever listing it read&lt;br /&gt;
zero rows, took the isEmpty branch and deleted the doc from Solr.&lt;br /&gt;
&lt;br /&gt;
Switched to @TransactionalEventListener(phase = AFTER_COMMIT). All publish&lt;br /&gt;
sites (TagListingController, PriceDropController, IndentController,&lt;br /&gt;
InventoryController, ModelHotDealServiceImpl) are inside a @Transactional&lt;br /&gt;
boundary, so no event is dropped.&lt;/div&gt;~ /trunk/profitmandi-dao/src/main/java/com/spice/profitmandi/dao/event/TagListingChangeListener.java&lt;br /&gt;</description><link>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37462</link><guid>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37462</guid></item>
<item><pubDate>Sat, 29 Aug 2026 16:19:05 +0530</pubDate><dc:creator>ranu</dc:creator><title>Rev 37461 – rbm drr dashboard new ui looks</title><description>&lt;div&gt;&lt;strong&gt;ranu – 1 file(s) modified&lt;/strong&gt;&lt;br/&gt;rbm drr dashboard new ui looks&lt;/div&gt;+ /trunk/profitmandi-dao/src/main/java/com/spice/profitmandi/dao/model/RbmBrandPartnerCountModel.java&lt;br /&gt;</description><link>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37461</link><guid>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37461</guid></item>
<item><pubDate>Sat, 29 Aug 2026 16:15:18 +0530</pubDate><dc:creator>ranu</dc:creator><title>Rev 37460 – rbm drr dashboard new ui looks</title><description>&lt;div&gt;&lt;strong&gt;ranu – 4 file(s) modified&lt;/strong&gt;&lt;br/&gt;rbm drr dashboard new ui looks&lt;/div&gt;~ /trunk/profitmandi-dao/src/main/java/com/spice/profitmandi/service/RbmTargetService.java&lt;br /&gt;~ /trunk/profitmandi-dao/src/main/java/com/spice/profitmandi/service/RbmTargetServiceImpl.java&lt;br /&gt;~ /trunk/profitmandi-fofo/src/main/java/com/spice/profitmandi/web/controller/monitors/RbmDrrDashboardController.java&lt;br /&gt;~ /trunk/profitmandi-fofo/src/main/webapp/WEB-INF/views/ftl/rbm-drr-dashboard.vm&lt;br /&gt;</description><link>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37460</link><guid>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37460</guid></item>
<item><pubDate>Sat, 29 Aug 2026 14:31:10 +0530</pubDate><dc:creator>amit</dc:creator><title>Rev 37459 – Motorola IMEI activation: secondary + tertiary in one browser session ...</title><description>&lt;div&gt;&lt;strong&gt;amit – 4 file(s) modified&lt;/strong&gt;&lt;br/&gt;Motorola IMEI activation: secondary + tertiary in one browser session&lt;br /&gt;
&lt;br /&gt;
Mirrors the oppo/realme combined jobs. MotorolaImeiActivationService drives&lt;br /&gt;
CheckMotorolaWarrantyTask, with MotorolaChallengeSolver for the challenge.&lt;br /&gt;
&lt;br /&gt;
Cadence comes from the pool query, which defers an imei for `days` after each&lt;br /&gt;
attempt (saveActivation bumps createTimestamp even when no date came back), so&lt;br /&gt;
days=2 retries everything every two days. Pending pool measured 1,534&lt;br /&gt;
(1,182 secondary + 352 tertiary); at ~10-14s/imei, 60 per invocation is about&lt;br /&gt;
12 minutes of driver time and clearing the pool inside 48h needs roughly 26&lt;br /&gt;
invocations, i.e. an OS cron entry every 90 minutes.&lt;br /&gt;
&lt;br /&gt;
Do NOT schedule it inside the oppo/realme window: each driver tree costs&lt;br /&gt;
~850MB and this box has been OOM-killed twice with tomcat the victim, so peak&lt;br /&gt;
concurrent drivers is the number that matters.&lt;/div&gt;+ /trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/scheduled/CheckMotorolaWarrantyTask.java&lt;br /&gt;+ /trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/scheduled/MotorolaChallengeSolver.java&lt;br /&gt;+ /trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/scheduled/MotorolaImeiActivationService.java&lt;br /&gt;~ /trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/scheduled/StandAlone.java&lt;br /&gt;</description><link>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37459</link><guid>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37459</guid></item>
<item><pubDate>Sat, 29 Aug 2026 13:22:56 +0530</pubDate><dc:creator>aman</dc:creator><title>Rev 37458 – LOI payment screenshot: report upload failures instead of reporting the ...</title><description>&lt;div&gt;&lt;strong&gt;aman – 6 file(s) modified&lt;/strong&gt;&lt;br/&gt;LOI payment screenshot: report upload failures instead of reporting the field as missing&lt;br /&gt;
&lt;br /&gt;
Submit validated the hidden #payment-sc-docId, but a file input keeps displaying the&lt;br /&gt;
chosen filename regardless of what happens next - so a failed /document-upload left the&lt;br /&gt;
field looking attached to the operator and missing to the validator (&quot;The following&lt;br /&gt;
fields are required: Payment screenshot&quot; on a form that was in fact filled in).&lt;br /&gt;
&lt;br /&gt;
The failure was silent three ways: doAjaxUploadRequestHandler had no error handler; the&lt;br /&gt;
global ajaxError reporter calls bootbox, which loads from cdnjs and throws if that is&lt;br /&gt;
blocked; and the confirm(&apos;Confirm file upload ?&apos;) gate returned false with no trace once&lt;br /&gt;
Chrome&apos;s &quot;prevent this page from creating additional dialogs&quot; box was ticked.&lt;br /&gt;
&lt;br /&gt;
- common.js: optional error callback on doAjaxUploadRequestHandler (additive, opts out of&lt;br /&gt;
  the generic global dialog via suppressGlobalError); uploadErrorMessage() extracts the&lt;br /&gt;
  server&apos;s real reason from the JSON body or the plain-text duplicate-request body;&lt;br /&gt;
  uploadDocument() pre-checks size/type, verifies a document_id actually came back, and&lt;br /&gt;
  reports a message naming the file - .HEIC and .webp get specific guidance;&lt;br /&gt;
  showAlert() falls back to native alert when bootbox is absent.&lt;br /&gt;
- loi-form.js: drop the confirm() gate, &apos;input&apos; -&gt; &apos;change&apos;, and clear the file input&lt;br /&gt;
  whenever the id does not land so the widget can never claim an unuploaded file. Status&lt;br /&gt;
  line under the field (Uploading... / Attached: name / Not attached). Submit now&lt;br /&gt;
  distinguishes not-picked, still-uploading and upload-failed. Same treatment for the&lt;br /&gt;
  OnboardingDocumentTable and mk_brand-fee-collection-details uploads, and the brand-fee&lt;br /&gt;
  dialog names the Attachment field instead of echoing the raw form key.&lt;br /&gt;
- loi-form.vm, pendingForm.vm: accept=&quot;...&quot; on both file inputs plus the status span.&lt;br /&gt;
- FileUploadController: reject empty files, report actual size against the actual limit,&lt;br /&gt;
  and resolveExtension() falls back to the filename when the browser sends a generic MIME&lt;br /&gt;
  type (Android file managers send application/octet-stream for ordinary JPG/PNG/PDF).&lt;br /&gt;
- AppConfig: static resource version 395 -&gt; 396 for the JS change.&lt;/div&gt;~ /trunk/profitmandi-fofo/src/main/java/com/spice/profitmandi/web/config/AppConfig.java&lt;br /&gt;~ /trunk/profitmandi-fofo/src/main/java/com/spice/profitmandi/web/controller/FileUploadController.java&lt;br /&gt;~ /trunk/profitmandi-fofo/src/main/webapp/resources/js/common.js&lt;br /&gt;~ /trunk/profitmandi-fofo/src/main/webapp/resources/js/loi-form.js&lt;br /&gt;~ /trunk/profitmandi-fofo/src/main/webapp/WEB-INF/views/ftl/loi-form.vm&lt;br /&gt;~ /trunk/profitmandi-fofo/src/main/webapp/WEB-INF/views/ftl/pendingForm.vm&lt;br /&gt;</description><link>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37458</link><guid>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37458</guid></item>
<item><pubDate>Sat, 29 Aug 2026 07:49:43 +0530</pubDate><dc:creator>amit</dc:creator><title>Rev 37457 – Lock the KYC state to the GSTIN on the LOI ...</title><description>&lt;div&gt;&lt;strong&gt;amit – 4 file(s) modified&lt;/strong&gt;&lt;br/&gt;Lock the KYC state to the GSTIN on the LOI&lt;br /&gt;
&lt;br /&gt;
The LOI already captures and validates a GSTIN, and the first two characters of a&lt;br /&gt;
GSTIN are its GST state code - so once the LOI exists the KYC state is a fact&lt;br /&gt;
about that number, not something to re-type. When one is present the state&lt;br /&gt;
dropdown is prefilled from it and locked; otherwise it stays a normal&lt;br /&gt;
master-backed select.&lt;br /&gt;
&lt;br /&gt;
Derived from the GSTIN rather than loi_form.gstState: that column is typed by&lt;br /&gt;
hand and holds values like &apos;UP&apos;, &apos;UTTAR PARDESH&apos; and &apos;MADHAY PARDESH&apos; which&lt;br /&gt;
resolve against nothing, and it is blank on 3 rows. Checked against prod - all&lt;br /&gt;
693 GSTINs on record are 15 characters and every one resolves through&lt;br /&gt;
statemaster.stateCode, and in all 14 rows where the two disagree the GSTIN is&lt;br /&gt;
right and the typed value is wrong or empty.&lt;br /&gt;
&lt;br /&gt;
The lock is per row, not per page: the KYC screens list many onboardings, so the&lt;br /&gt;
derived state is passed as a map keyed by onboarding id. Blank rows carry their&lt;br /&gt;
onboarding id so a new KYC entry is locked too, which is the case that matters.&lt;br /&gt;
&lt;br /&gt;
data-gst-locked keeps the row&apos;s dblclick edit toggle from re-enabling the field;&lt;br /&gt;
the value still submits because the JS reads it explicitly with .val().&lt;/div&gt;~ /trunk/profitmandi-fofo/src/main/java/com/spice/profitmandi/web/controller/PartnerOnBoardingPanelController.java&lt;br /&gt;~ /trunk/profitmandi-fofo/src/main/webapp/resources/js/panel-listing.js&lt;br /&gt;~ /trunk/profitmandi-fofo/src/main/webapp/WEB-INF/views/ftl/fofo-kyc-row.vm&lt;br /&gt;~ /trunk/profitmandi-fofo/src/main/webapp/WEB-INF/views/ftl/legal-kyc-row.vm&lt;br /&gt;</description><link>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37457</link><guid>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37457</guid></item>
<item><pubDate>Sat, 29 Aug 2026 07:42:55 +0530</pubDate><dc:creator>amit</dc:creator><title>Rev 37456 – Block billing when NIC rejects the transporter GSTIN for e-way ...</title><description>&lt;div&gt;&lt;strong&gt;amit – 6 file(s) modified&lt;/strong&gt;&lt;br/&gt;Block billing when NIC rejects the transporter GSTIN for e-way bills&lt;br /&gt;
&lt;br /&gt;
NIC files the e-way bill alongside the IRN, so a deregistered transporter&lt;br /&gt;
GSTIN returns Status=1 with InfoDtls[InfCd=EWBERR] (3029 &quot;GSTIN - ... is&lt;br /&gt;
not active&quot;): the IRN is valid while ewb_no stays null. Nothing downstream&lt;br /&gt;
reads that as a failure, so invoices kept being issued for goods that&lt;br /&gt;
could not legally move.&lt;br /&gt;
&lt;br /&gt;
Cache the rejected transporter GSTIN in Redis and refuse to bill through&lt;br /&gt;
it. The block is keyed on the GSTIN, since one GSTIN is shared by several&lt;br /&gt;
warehouse_provider rows, and it carries the day it was raised so it lapses&lt;br /&gt;
at midnight and each new day re-tests NIC once. Correcting the GSTIN in&lt;br /&gt;
the provider panel lifts it immediately.&lt;br /&gt;
&lt;br /&gt;
Only errors that are the transporter&apos;s fault block billing - NIC&apos;s 3029,&lt;br /&gt;
or any message naming the GSTIN we sent as TransId. Every other EWBERR&lt;br /&gt;
behaves as before: the IRN is filed and the e-way bill is retried later.&lt;br /&gt;
&lt;br /&gt;
Guard sits in addBillingDetailsForGrouppedOrders before the pessimistic&lt;br /&gt;
lock and before any mutation, mirroring LogisticsServiceImpl#getEwbDetails&lt;br /&gt;
(order&apos;s own warehouse; self-pickup and runner skipped, as they travel on&lt;br /&gt;
a vehicle number rather than a transporter id).&lt;/div&gt;~ /trunk/profitmandi-common/src/main/java/com/spice/profitmandi/common/model/ProfitMandiConstants.java&lt;br /&gt;+ /trunk/profitmandi-dao/src/main/java/com/spice/profitmandi/service/integrations/gstpro/EwbTransporterFailureCache.java&lt;br /&gt;~ /trunk/profitmandi-dao/src/main/java/com/spice/profitmandi/service/integrations/gstpro/GstProService.java&lt;br /&gt;~ /trunk/profitmandi-dao/src/main/java/com/spice/profitmandi/service/transaction/TransactionServiceImpl.java&lt;br /&gt;+ /trunk/profitmandi-dao/src/test/java/com/spice/profitmandi/service/integrations/gstpro/EwbTransporterFailureCacheTest.java&lt;br /&gt;~ /trunk/profitmandi-fofo/src/main/java/com/spice/profitmandi/web/controller/provider/ProviderController.java&lt;br /&gt;</description><link>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37456</link><guid>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37456</guid></item>
<item><pubDate>Sat, 29 Aug 2026 07:31:14 +0530</pubDate><dc:creator>amit</dc:creator><title>Rev 37455 – Make the KYC state a master-backed dropdown instead of free ...</title><description>&lt;div&gt;&lt;strong&gt;amit – 5 file(s) modified&lt;/strong&gt;&lt;br/&gt;Make the KYC state a master-backed dropdown instead of free text&lt;br /&gt;
&lt;br /&gt;
fofo-kyc-row.vm and legal-kyc-row.vm captured state as a free-text input, so&lt;br /&gt;
nothing stopped a spelling the master does not hold. That state is resolved back&lt;br /&gt;
through inventory.statemaster by name later, and an unresolvable one cannot be&lt;br /&gt;
turned into a state code. Both now render a $stateNames select, and all five&lt;br /&gt;
render paths in PartnerOnBoardingPanelController supply the list.&lt;br /&gt;
&lt;br /&gt;
panel-listing.js reads these fields positionally, by td index and&lt;br /&gt;
input[type=&apos;text&apos;]. A select does not match that selector, so the value would&lt;br /&gt;
have come back undefined and KYC would have saved a blank state silently -&lt;br /&gt;
both selectors now target the select. Row editing is unaffected: the enable&lt;br /&gt;
toggle uses :input, which covers select.&lt;br /&gt;
&lt;br /&gt;
Bump the asset version for the panel-listing.js change.&lt;/div&gt;~ /trunk/profitmandi-fofo/src/main/java/com/spice/profitmandi/web/config/AppConfig.java&lt;br /&gt;~ /trunk/profitmandi-fofo/src/main/java/com/spice/profitmandi/web/controller/PartnerOnBoardingPanelController.java&lt;br /&gt;~ /trunk/profitmandi-fofo/src/main/webapp/resources/js/panel-listing.js&lt;br /&gt;~ /trunk/profitmandi-fofo/src/main/webapp/WEB-INF/views/ftl/fofo-kyc-row.vm&lt;br /&gt;~ /trunk/profitmandi-fofo/src/main/webapp/WEB-INF/views/ftl/legal-kyc-row.vm&lt;br /&gt;</description><link>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37455</link><guid>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37455</guid></item>
<item><pubDate>Sat, 29 Aug 2026 07:24:40 +0530</pubDate><dc:creator>amit</dc:creator><title>Rev 37454 – Build googleMailSender as AuthenticatedIdentityMailSender  The bean authenticates as sdtech@smartdukaan.com, ...</title><description>&lt;div&gt;&lt;strong&gt;amit – 1 file(s) modified&lt;/strong&gt;&lt;br/&gt;Build googleMailSender as AuthenticatedIdentityMailSender&lt;br /&gt;
&lt;br /&gt;
The bean authenticates as &lt;a href=&quot;mailto:sdtech@smartdukaan.com&quot;&gt;sdtech@smartdukaan.com&lt;/a&gt;, so Google Workspace only permits&lt;br /&gt;
that address in From. Callers hardcode noreply@, which Gmail refuses with 535&lt;br /&gt;
&apos;Authenticated user is not authorized to send mail&apos;, silently dropping every alert&lt;br /&gt;
sent through it. The sender now rewrites From to its own username at send time.&lt;br /&gt;
&lt;br /&gt;
See profitmandi-common r37451.&lt;/div&gt;~ /trunk/profitmandi-web/src/main/java/com/spice/profitmandi/web/config/AppConfig.java&lt;br /&gt;</description><link>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37454</link><guid>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37454</guid></item>
<item><pubDate>Sat, 29 Aug 2026 07:24:39 +0530</pubDate><dc:creator>amit</dc:creator><title>Rev 37453 – Build googleMailSender as AuthenticatedIdentityMailSender  The bean authenticates as sdtech@smartdukaan.com, ...</title><description>&lt;div&gt;&lt;strong&gt;amit – 1 file(s) modified&lt;/strong&gt;&lt;br/&gt;Build googleMailSender as AuthenticatedIdentityMailSender&lt;br /&gt;
&lt;br /&gt;
The bean authenticates as &lt;a href=&quot;mailto:sdtech@smartdukaan.com&quot;&gt;sdtech@smartdukaan.com&lt;/a&gt;, so Google Workspace only permits&lt;br /&gt;
that address in From. Callers hardcode noreply@, which Gmail refuses with 535&lt;br /&gt;
&apos;Authenticated user is not authorized to send mail&apos;, silently dropping every alert&lt;br /&gt;
sent through it. The sender now rewrites From to its own username at send time.&lt;br /&gt;
&lt;br /&gt;
See profitmandi-common r37451.&lt;/div&gt;~ /trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/Application.java&lt;br /&gt;</description><link>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37453</link><guid>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37453</guid></item>
<item><pubDate>Sat, 29 Aug 2026 07:24:31 +0530</pubDate><dc:creator>amit</dc:creator><title>Rev 37452 – Fix e-invoice IRN failure when the NIC auth token is ...</title><description>&lt;div&gt;&lt;strong&gt;amit – 2 file(s) modified&lt;/strong&gt;&lt;br/&gt;Fix e-invoice IRN failure when the NIC auth token is about to expire&lt;br /&gt;
&lt;br /&gt;
NIC reissues the same auth token until it genuinely expires, so evicting the cache&lt;br /&gt;
inside the 5 minute leeway hands back the identical near-dead token. getAuthenticatedHeaders&lt;br /&gt;
checked the leeway once, before that re-fetch, and never inspected what came back.&lt;br /&gt;
NSUPHR1770 filed with a token that had 12 seconds of life left and the gateway answered&lt;br /&gt;
412/GSP752 &apos;eInvoice AuthToken not found or expired&apos;; the invoice filed a minute later,&lt;br /&gt;
past the expiry and so on a genuinely new token, succeeded.&lt;br /&gt;
&lt;br /&gt;
getUsableAuthToken re-checks the refetched token and, when it is still inside a 60s&lt;br /&gt;
floor, waits out the remainder before minting again. The wait is bounded by that floor&lt;br /&gt;
and every caller on this path is a cron/async thread already running against a 60s NIC&lt;br /&gt;
timeout with no transaction held open.&lt;br /&gt;
&lt;br /&gt;
Also make the gateway&apos;s own failures legible. NIC rejections arrive as&lt;br /&gt;
{Status, ErrorDetails}, but failures the ASP raises in front of NIC use&lt;br /&gt;
{status_cd, error{error_cd, message}}, which shares no field name with RespPl: Gson&lt;br /&gt;
produced an all-null object and the stored reason degraded to the literal&lt;br /&gt;
&apos;RespPl{Status=0, Data=null, ErrorDetails=null, InfoDtls=null}&apos;, indistinguishable from&lt;br /&gt;
NIC rejecting the document. RespGSPErr already modelled that envelope but was a&lt;br /&gt;
non-static inner class and so could not be instantiated by Gson; made it static and&lt;br /&gt;
copy the code and message into ErrorDetails, which every caller already reads.&lt;/div&gt;~ /trunk/profitmandi-dao/src/main/java/com/spice/profitmandi/service/integrations/gstpro/api/model/E_InvoiceCommon.java&lt;br /&gt;~ /trunk/profitmandi-dao/src/main/java/com/spice/profitmandi/service/integrations/gstpro/GstProAuthService.java&lt;br /&gt;</description><link>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37452</link><guid>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37452</guid></item>
<item><pubDate>Sat, 29 Aug 2026 07:24:19 +0530</pubDate><dc:creator>amit</dc:creator><title>Rev 37451 – Send authenticated mail as the account that actually authenticates  ...</title><description>&lt;div&gt;&lt;strong&gt;amit – 1 file(s) modified&lt;/strong&gt;&lt;br/&gt;Send authenticated mail as the account that actually authenticates&lt;br /&gt;
&lt;br /&gt;
googleMailSender logs in to smtp.gmail.com as &lt;a href=&quot;mailto:sdtech@smartdukaan.com&quot;&gt;sdtech@smartdukaan.com&lt;/a&gt;, but callers&lt;br /&gt;
build their From as &lt;a href=&quot;mailto:noreply@smartdukaan.com&quot;&gt;noreply@smartdukaan.com&lt;/a&gt;. Google Workspace binds an authenticated&lt;br /&gt;
session to one identity and refuses the mismatch with 535 &apos;Authorization failed:&lt;br /&gt;
Authenticated user is not authorized to send mail&apos; - the password is accepted, the&lt;br /&gt;
message is not. Every alert routed through that sender was therefore lost at the SMTP&lt;br /&gt;
layer, the e-invoice failure alert for NSUPHR1770 among them.&lt;br /&gt;
&lt;br /&gt;
AuthenticatedIdentityMailSender rewrites From to the sender&apos;s own username at send&lt;br /&gt;
time, keeping whatever display name the caller chose. Senders that do not authenticate&lt;br /&gt;
are left alone: gmailRelaySender is the IP-authorised relay and has no username, and&lt;br /&gt;
sendGridMailSender&apos;s username is the literal &apos;apikey&apos;, so both keep noreply@.&lt;br /&gt;
&lt;br /&gt;
Enforced in the sender rather than at the ~40 call sites that build their own&lt;br /&gt;
MimeMessage and hardcode noreply@, so it covers the ones added next too.&lt;/div&gt;+ /trunk/profitmandi-common/src/main/java/com/spice/profitmandi/common/services/AuthenticatedIdentityMailSender.java&lt;br /&gt;</description><link>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37451</link><guid>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37451</guid></item>
<item><pubDate>Sat, 29 Aug 2026 07:17:00 +0530</pubDate><dc:creator>amit</dc:creator><title>Rev 37450 – Oppo: find only the hole, and let a low-contrast hole ...</title><description>&lt;div&gt;&lt;strong&gt;amit – 1 file(s) modified&lt;/strong&gt;&lt;br/&gt;Oppo: find only the hole, and let a low-contrast hole be found at all&lt;br /&gt;
&lt;br /&gt;
60% of oppo attempts never got past detection -- getMatCircles2 returned null and&lt;br /&gt;
the attempt was wasted before any aiming happened. That is a bigger loss than&lt;br /&gt;
everything the scheduling work addressed put together.&lt;br /&gt;
&lt;br /&gt;
Two causes, both visible once real puzzles are captured and looked at.&lt;br /&gt;
&lt;br /&gt;
1. param1 is the Canny HIGH threshold, and it was 100. Edges weaker than that are&lt;br /&gt;
   discarded before circle finding begins. Measured hole-vs-background contrast on&lt;br /&gt;
   four live puzzles: 98, 103, 28, 117. The 28 -- a pale lilac background -- cannot&lt;br /&gt;
   produce an edge at 100, so no circle exists to find. Dropped to 50.&lt;br /&gt;
&lt;br /&gt;
2. It insisted on TWO circles whose radii matched within 3px, and returned null&lt;br /&gt;
   otherwise. So a puzzle where the hole was located perfectly still failed if the&lt;br /&gt;
   ring was missed, or if the two radii differed by 4px.&lt;br /&gt;
&lt;br /&gt;
The second is the sillier one: the ring is a DOM element,&lt;br /&gt;
dx_captcha_basic_sub-slider_, whose exact position aim() already reads via&lt;br /&gt;
pieceCentreX(). Detection was re-finding something known exactly, and then&lt;br /&gt;
throwing away a good hole because it could not confirm it. Now the ring position&lt;br /&gt;
is passed in and hough only has to find one thing.&lt;br /&gt;
&lt;br /&gt;
Lowering param1 without the pairing check would let textured backgrounds (the&lt;br /&gt;
sand images especially) supply spurious circles with nothing to reject them, so&lt;br /&gt;
the candidate is verified as an actual hole: its core must be at least 15&lt;br /&gt;
luminance below the frame mean. Real holes measure 28-117 below, so 15 rejects&lt;br /&gt;
noise with margin.&lt;br /&gt;
&lt;br /&gt;
Validated against live puzzles before committing:&lt;br /&gt;
  ring is at rel 42px on every sample; hole lands at 113-194px, separation&lt;br /&gt;
  71-152px -- so RING_EXCLUSION_PX = 30 never rejects a real hole&lt;br /&gt;
  hole contrast 28-117 against MIN_HOLE_DARKNESS = 15&lt;br /&gt;
&lt;br /&gt;
Not validated locally: the hough call itself. opencv 3.4.2-0 ships no osx/arm64&lt;br /&gt;
native and every JDK on this machine is arm64, so the detection path cannot run&lt;br /&gt;
here. param1 = 50 is reasoned from the measured contrasts, not measured. If the&lt;br /&gt;
detection rate does not move, 40 is the next value to try -- the darkness check is&lt;br /&gt;
what makes going lower safe.&lt;br /&gt;
&lt;br /&gt;
Watch &quot;Detected N circle(s) but all sat on the ring&quot; and &quot;Rejecting circle at Npx&quot;&lt;br /&gt;
in the log: the first means hough found nothing but the ring, the second means the&lt;br /&gt;
darkness check is doing its job.&lt;/div&gt;~ /trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/scheduled/CheckOppoWarrantyTask.java&lt;br /&gt;</description><link>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37450</link><guid>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37450</guid></item>
<item><pubDate>Fri, 28 Aug 2026 20:25:12 +0530</pubDate><dc:creator>amit</dc:creator><title>Rev 37449 – Realme unmerged too: four selenium jobs, one per pool per ...</title><description>&lt;div&gt;&lt;strong&gt;amit – 1 file(s) modified&lt;/strong&gt;&lt;br/&gt;Realme unmerged too: four selenium jobs, one per pool per brand&lt;br /&gt;
&lt;br /&gt;
r37447 merged secondary and tertiary per brand to halve concurrent drivers.&lt;br /&gt;
r37448 reverted that for oppo, because serialising cost it more per imei than a&lt;br /&gt;
second browser did. This reverts it for realme as well, which ends the merge&lt;br /&gt;
experiment entirely.&lt;br /&gt;
&lt;br /&gt;
What the merge actually cost realme is only visible now that oppo is parallel&lt;br /&gt;
again: realme&apos;s per-imei went 14.2s -&gt; 29s. It did not change behaviour -- it is&lt;br /&gt;
simply competing with oppo&apos;s two browsers for CPU. At 29s its merged ceiling is&lt;br /&gt;
86400/29 = 2,979/day, just under the 3,054/day the daily re-check needs, so no&lt;br /&gt;
batch size could have closed the gap. Two parallel pools restore the ~3,900/day&lt;br /&gt;
it managed historically at four drivers.&lt;br /&gt;
&lt;br /&gt;
Measured before this change (25 min window):&lt;br /&gt;
&lt;br /&gt;
  brand    needed/day   throughput   note&lt;br /&gt;
  Oppo         8,838       7,661     parallel revert worked, +122%&lt;br /&gt;
  Realme       3,054       1,843     merged and CPU-starved&lt;br /&gt;
  Vivo        10,082      16,128     surplus, cannot transfer to another brand&lt;br /&gt;
&lt;br /&gt;
Sizes unchanged: oppo 25 per pool, realme 12 per pool, vivo 50+10.&lt;br /&gt;
&lt;br /&gt;
Honest accounting of the merge: it was my idea, sized on per-brand arithmetic that&lt;br /&gt;
ignored contention between brands, and it is now fully reverted. What survives&lt;br /&gt;
from that line of work is the part that actually paid -- reaping orphaned browsers&lt;br /&gt;
(~1.8GB), the per-brand retry caps, and per-brand maxResults. Peak drivers are&lt;br /&gt;
back to 4, which is where they started.&lt;br /&gt;
&lt;br /&gt;
Watch for contention: four browsers is the configuration that produced the 29s&lt;br /&gt;
figure for realme in the first place, so oppo may slow from its current 19.7s.&lt;br /&gt;
Re-measure both before tuning sizes again.&lt;/div&gt;~ /trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/scheduled/ScheduledSkeleton.java&lt;br /&gt;</description><link>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37449</link><guid>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37449</guid></item>
<item><pubDate>Fri, 28 Aug 2026 12:08:39 +0530</pubDate><dc:creator>amit</dc:creator><title>Rev 37448 – Daily re-check for all brands; Oppo back to parallel pools ...</title><description>&lt;div&gt;&lt;strong&gt;amit – 3 file(s) modified&lt;/strong&gt;&lt;br/&gt;Daily re-check for all brands; Oppo back to parallel pools&lt;br /&gt;
&lt;br /&gt;
Two changes.&lt;br /&gt;
&lt;br /&gt;
1. Re-check window 4 days (secondary) and 2 days (tertiary) -&gt; 1 day everywhere.&lt;br /&gt;
   Daily demand becomes the full universe rather than a fraction of it:&lt;br /&gt;
&lt;br /&gt;
     Oppo    4,798 + 4,040 =  8,838/day&lt;br /&gt;
     Vivo    9,407 +   675 = 10,082/day   (doing 15,345 -- fine)&lt;br /&gt;
     Realme  1,973 + 1,081 =  3,054/day&lt;br /&gt;
&lt;br /&gt;
2. Oppo&apos;s two pools run in PARALLEL again, reverting the merge in r37447 for that&lt;br /&gt;
   brand only. Realme stays merged.&lt;br /&gt;
&lt;br /&gt;
   The merge was a straight trade of throughput for memory and oppo could not&lt;br /&gt;
   afford it. Measured over 32 minutes and again over an hour the next morning:&lt;br /&gt;
   3,555 then 3,456/day against 5,280 before merging. Batch cadence settled at a&lt;br /&gt;
   very regular ~15.5 min per cycle, so a 30-imei merged batch takes ~10.5 min =&lt;br /&gt;
   ~21s/imei, against the 10.2s it managed unmerged. At 21s the ceiling is&lt;br /&gt;
   86400/21 = 4,114/day even with zero idle, so no batch size and no shorter&lt;br /&gt;
   fixedDelay could have reached 8,838. Serialising simply costs more per imei&lt;br /&gt;
   here than running two browsers does.&lt;br /&gt;
&lt;br /&gt;
   Realme keeps the merge: it needs 3,054/day and delivers 2,952 merged, so a&lt;br /&gt;
   small size bump covers it without a second browser.&lt;br /&gt;
&lt;br /&gt;
Sizes: oppo 25 per pool (2 jobs in parallel), realme 12+12 merged, vivo 50+10&lt;br /&gt;
unchanged. Vivo has already cleared its entire secondary backlog -- the pool&lt;br /&gt;
reads 0 and both lists come back empty -- which is what the batch of 50 was for.&lt;br /&gt;
&lt;br /&gt;
Cost: oppo goes back to two concurrent drivers, so the fleet is 3 rather than 2,&lt;br /&gt;
roughly +700MB. Acceptable against the ~2GB freed today by reaping orphaned&lt;br /&gt;
browsers and capping retries, but it is the reason realme was left merged.&lt;br /&gt;
&lt;br /&gt;
Sizes are a starting point, not a final answer: oppo&apos;s per-imei time differs&lt;br /&gt;
markedly between merged and parallel modes, so re-measure before tuning further.&lt;/div&gt;~ /trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/scheduled/ScheduledSkeleton.java&lt;br /&gt;~ /trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/scheduled/StandAlone.java&lt;br /&gt;~ /trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/scheduled/VivoImeiActivationService.java&lt;br /&gt;</description><link>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37448</link><guid>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37448</guid></item>
<item><pubDate>Thu, 27 Aug 2026 20:36:36 +0530</pubDate><dc:creator>amit</dc:creator><title>Rev 37447 – Selenium: one browser per brand instead of one per pool ...</title><description>&lt;div&gt;&lt;strong&gt;amit – 2 file(s) modified&lt;/strong&gt;&lt;br/&gt;Selenium: one browser per brand instead of one per pool&lt;br /&gt;
&lt;br /&gt;
Oppo and Realme each ran secondary and tertiary as separate @Scheduled jobs, so&lt;br /&gt;
each brand opened two ChromeDrivers at once and the fleet ran four. Each driver&lt;br /&gt;
tree costs roughly 850MB. This box co-hosts a 9.4GB tomcat with ~3GB available&lt;br /&gt;
and has been OOM-killed twice this month -- tomcat was the victim both times, so&lt;br /&gt;
peak concurrency is the thing that matters here.&lt;br /&gt;
&lt;br /&gt;
Combined into one job per brand. Nothing downstream changes: the two pools differ&lt;br /&gt;
only in which named query fills them, and both already funnel into the same&lt;br /&gt;
updateActivationDate -&gt; checkWarranty -&gt; saveActivation path. They are disjoint by&lt;br /&gt;
construction (secondary excludes anything with a FofoLineItem, tertiary is&lt;br /&gt;
FofoLineItem-based); distinct() is insurance, not a fix for a known overlap.&lt;br /&gt;
&lt;br /&gt;
Sizing matters, because merging SERIALISES work that used to run in parallel and&lt;br /&gt;
keeping the old batch sizes would quietly cost throughput. Measured post-cap at&lt;br /&gt;
10.2s/imei (oppo, down from 14.6 after r37445) and 14.2s/imei (realme), solving&lt;br /&gt;
M * 86400 / (300 + M*t):&lt;br /&gt;
&lt;br /&gt;
  oppo    2 parallel jobs x10 = 4,299/day  -&gt;  merged 15+15 = 4,277/day  (parity)&lt;br /&gt;
  realme  2 parallel jobs x10 = 3,910/day  -&gt;  merged 10+10 = 2,959/day  (-24%)&lt;br /&gt;
&lt;br /&gt;
Oppo is sized to hold parity because it is already short of its 4,798/day need.&lt;br /&gt;
Realme is left at 20 -- it needs 2,243/day, so it can absorb the dip in exchange&lt;br /&gt;
for shorter batches and a shorter-lived browser.&lt;br /&gt;
&lt;br /&gt;
What this saves and does not save: total driver-SECONDS are roughly unchanged,&lt;br /&gt;
which is the point of resizing. PEAK concurrent drivers halves from 4 to 2.&lt;br /&gt;
&lt;br /&gt;
Also skips starting a browser at all when both pools come back empty -- currently&lt;br /&gt;
never true, but it costs nothing and a browser launched to do nothing is pure&lt;br /&gt;
waste on this box.&lt;br /&gt;
&lt;br /&gt;
checkOppoImeiStatus/Tertiary and the realme equivalents are left in place for&lt;br /&gt;
manual invocation; they are simply no longer scheduled.&lt;/div&gt;~ /trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/scheduled/ScheduledSkeleton.java&lt;br /&gt;~ /trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/scheduled/StandAlone.java&lt;br /&gt;</description><link>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37447</link><guid>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37447</guid></item>
</channel></rss>