apiVersion: jenkins.io/v1 kind: PipelineActivity metadata: annotations: lighthouse.jenkins-x.io/cloneURI: https://github.com/greencapitaltrade/bifrost.git lighthouse.jenkins-x.io/job: release pipeline.jenkins-x.io/traceID: 98b5fa2be250a73e841fe4aada36c8be creationTimestamp: "2026-05-31T05:00:27Z" generation: 8 labels: branch: main build: "110" context: release created-by-lighthouse: "true" event-GUID: 9e5d543a-5cad-11f1-8a15-7fad1cb061ff lighthouse.jenkins-x.io/baseSHA: ffce71320845d31a43d47154a6d86d6f9f9c332d lighthouse.jenkins-x.io/branch: main lighthouse.jenkins-x.io/buildNum: "1780203627377" lighthouse.jenkins-x.io/context: release lighthouse.jenkins-x.io/id: apitaltrade-bifrost-main-release-hwbxd lighthouse.jenkins-x.io/job: release lighthouse.jenkins-x.io/lastCommitSHA: ffce71320845d31a43d47154a6d86d6f9f9c332d lighthouse.jenkins-x.io/refs.org: greencapitaltrade lighthouse.jenkins-x.io/refs.repo: bifrost lighthouse.jenkins-x.io/type: postsubmit owner: greencapitaltrade podName: apitaltrade-bifrost-main-release-rl4dj-from-build-pack-pod provider: github repository: bifrost tekton.dev/pipeline: apitaltrade-bifrost-main-release-rl4dj managedFields: - apiVersion: jenkins.io/v1 fieldsType: FieldsV1 fieldsV1: f:metadata: f:labels: f:branch: {} f:context: {} f:owner: {} f:provider: {} f:repository: {} f:spec: f:lastCommitMessage: {} f:lastCommitSHA: {} f:releaseNotesURL: {} f:version: {} manager: jx-changelog-0.10.18 operation: Update time: "2026-05-31T05:05:06Z" - apiVersion: jenkins.io/v1 fieldsType: FieldsV1 fieldsV1: f:metadata: f:annotations: .: {} f:lighthouse.jenkins-x.io/cloneURI: {} f:lighthouse.jenkins-x.io/job: {} f:pipeline.jenkins-x.io/traceID: {} f:labels: .: {} f:build: {} f:created-by-lighthouse: {} f:event-GUID: {} f:lighthouse.jenkins-x.io/baseSHA: {} f:lighthouse.jenkins-x.io/branch: {} f:lighthouse.jenkins-x.io/buildNum: {} f:lighthouse.jenkins-x.io/context: {} f:lighthouse.jenkins-x.io/id: {} f:lighthouse.jenkins-x.io/job: {} f:lighthouse.jenkins-x.io/lastCommitSHA: {} f:lighthouse.jenkins-x.io/refs.org: {} f:lighthouse.jenkins-x.io/refs.repo: {} f:lighthouse.jenkins-x.io/type: {} f:podName: {} f:tekton.dev/pipeline: {} f:spec: .: {} f:baseSHA: {} f:batchPipelineActivity: {} f:build: {} f:buildLogsUrl: {} f:completedTimestamp: {} f:context: {} f:gitBranch: {} f:gitOwner: {} f:gitRepository: {} f:gitUrl: {} f:message: {} f:pipeline: {} f:startedTimestamp: {} f:status: {} f:steps: {} manager: jx-build-controller operation: Update time: "2026-05-31T05:06:07Z" name: greencapitaltrade-bifrost-main-110 namespace: jx resourceVersion: "64861855" uid: 123604fe-91b6-434c-87b0-23f8f3dcd93d spec: baseSHA: ffce71320845d31a43d47154a6d86d6f9f9c332d batchPipelineActivity: {} build: "110" buildLogsUrl: s3://logs-gct-prod-20260425045301534100000007/jenkins-x/logs/greencapitaltrade/bifrost/main/110.log completedTimestamp: "2026-05-31T05:05:59Z" context: release gitBranch: main gitOwner: greencapitaltrade gitRepository: bifrost gitUrl: https://github.com/greencapitaltrade/bifrost.git lastCommitMessage: | feat(asset_management): SIM tracking via Odoo MRP + sale + contract_sale chain Adds postpaid SIM tracking by reusing native Odoo mechanisms — no SIM- specific models, one custom FK on asset.iot.device, three stored computed fields on asset.asset. Background. Today there's no link between SIM ↔ device ↔ asset ↔ subscription. When a customer's asset subscription lapses, ops doesn't know which SIM ICCID to suspend at the carrier portal; when they renew, ops doesn't know which to reactivate. Operator keeps paying for connectivity on unpaid assets, or assets stay offline after renewal until somebody figures out the link manually. Approach. Devices and SIMs flow through standard Odoo: - Buy SIMs from carrier → stock.picking (in) → stock.lot per ICCID, tracking='serial'. - Buy devices → stock.picking (in) → stock.lot per IMEI. - Install SIM in device → mrp.production consumes 1 device + 1 SIM, produces 1 unit lot whose serial reuses the consumed device's IMEI. - Sale → stock.move to customer location, date_done = activation date. - Subscription tracked on contract.line (asset_subscription_* product code), linked to the sale via OCA contract_sale. - Suspend / reactivate SIM = ops calls the carrier portal + flips asset.iot.device.active. Single custom FK: asset.iot.device.stock_lot_id, with an @api.constrains keeping device_id == stock_lot_id.name in sync. From this FK the daily cron walks asset → device → unit lot → MRP → SIM lot to resolve the SIM ICCID without any SIM-specific model. Three stored computed fields on asset.asset (subscription_state, active_subscription_end_date, first_delivered_on) walk the chain unit lot → stock.move (customer delivery) → sale.order.line → contract.line (via contract_sale OCA). Stored so the digest cron filters in SQL, not Python. Daily digest cron at 09:00 IST produces two lists per company: (1) Suspend candidates — subscription lapsed/lapsing, device still active. Ops calls carrier to stop billing, flips device active. (2) Reactivate candidates — subscription renewed, device still inactive. Ops calls carrier to resume, flips device active. Module changes: - manifest depends now include contract_sale, mrp, sale_stock, product (already installed in our deployment; pinned for safety). - Version bump 18.0.2.3.0 → 18.0.2.4.0. - pre-init migration adds asset_iot_device.stock_lot_id column at raw-SQL level before Odoo's boot phase tries to SELECT it (see docs/feedback_inherited_model_pre_init memory). - New seed data: product.category tree (IoT/Devices, IoT/SIMs), product.attribute records (Carrier, Form Factor, SIM Slots) with starter values, and the ir.cron entry. - Three new model files registered in models/__init__.py. - Tests in tests/test_sim_tracking.py covering the FK constraint, get_sim_lots() walk, computed-field defaults, and the cron's suspend/reactivate selection. Onboarding runbook lives at docs/sim-tracking-onboarding.md — pre- flight checks, the MRP operation-type configuration ops needs to do, product seeding instructions, backfill procedure for existing devices, and day-2 ops procedures for suspend/reactivate and SIM swap. Full design rationale (why 1 field and not 5 custom models, why we reuse MRP, why we walk the sale chain instead of contract.line.asset_id) in docs/sim-tracking.md. Out of scope for this PR (follow-ups): - Backfill script for existing asset.iot.device rows. - mrp.bom pre_production hook to auto-copy device serial onto produced lot serial (Phase 1 has ops do this manually + constraint catches mistakes). - Fury GraphQL surface (assetIotUnits, assetsNeedingSimSuspend, …). - Flash UI (Expiring tab + asset detail IoT tab). - asset_management.email_sim_digest mail template (cron logs only until the template is added). lastCommitSHA: cc82c3981079c47fa7204a6e4d95cb2bcca9e059 message: 'Tasks Completed: 1 (Failed: 0, Cancelled 0), Skipped: 0' pipeline: greencapitaltrade/bifrost/main releaseNotesURL: https://github.com/greencapitaltrade/bifrost/releases/tag/v6.73.0 startedTimestamp: "2026-05-31T05:00:27Z" status: Succeeded steps: - kind: Stage stage: completedTimestamp: "2026-05-31T05:05:59Z" name: from build pack startedTimestamp: "2026-05-31T05:01:11Z" status: Succeeded steps: - completedTimestamp: "2026-05-31T05:01:31Z" name: Git Clone startedTimestamp: "2026-05-31T05:01:11Z" status: Succeeded - completedTimestamp: "2026-05-31T05:01:35Z" name: Next Version startedTimestamp: "2026-05-31T05:01:31Z" status: Succeeded - completedTimestamp: "2026-05-31T05:01:43Z" name: Jx Variables startedTimestamp: "2026-05-31T05:01:40Z" status: Succeeded - completedTimestamp: "2026-05-31T05:01:52Z" name: Setup Npm Nexus startedTimestamp: "2026-05-31T05:01:51Z" status: Succeeded - completedTimestamp: "2026-05-31T05:01:58Z" name: Setup Pip Cache startedTimestamp: "2026-05-31T05:01:56Z" status: Succeeded - completedTimestamp: "2026-05-31T05:02:00Z" name: Process Config Templates startedTimestamp: "2026-05-31T05:01:59Z" status: Succeeded - completedTimestamp: "2026-05-31T05:02:00Z" name: Update Fleet Management Version startedTimestamp: "2026-05-31T05:02:00Z" status: Succeeded - completedTimestamp: "2026-05-31T05:02:07Z" name: Check Registry startedTimestamp: "2026-05-31T05:02:05Z" status: Succeeded - completedTimestamp: "2026-05-31T05:04:50Z" name: Build Container Build startedTimestamp: "2026-05-31T05:02:18Z" status: Succeeded - completedTimestamp: "2026-05-31T05:05:06Z" name: Promote Changelog startedTimestamp: "2026-05-31T05:04:51Z" status: Succeeded - completedTimestamp: "2026-05-31T05:05:10Z" name: Promote Helm Release startedTimestamp: "2026-05-31T05:05:07Z" status: Succeeded - completedTimestamp: "2026-05-31T05:05:59Z" name: Promote Jx Promote startedTimestamp: "2026-05-31T05:05:11Z" status: Succeeded - kind: Promote promote: environment: staging pullRequest: pullRequestURL: https://github.com/greencapitaltrade/mcu/pull/4037 startedTimestamp: "2026-05-31T05:05:38Z" status: Succeeded startedTimestamp: "2026-05-31T05:05:38Z" status: Succeeded - kind: Promote promote: environment: production pullRequest: pullRequestURL: https://github.com/greencapitaltrade/mcu/pull/4038 startedTimestamp: "2026-05-31T05:05:56Z" status: Succeeded startedTimestamp: "2026-05-31T05:05:56Z" status: Succeeded version: 6.73.0 status: {}