|
| 1 | +import json |
| 2 | +import logging |
| 3 | +from datetime import datetime |
| 4 | + |
1 | 5 | from openupgradelib import openupgrade |
2 | 6 |
|
3 | | -# Companion to pre-migration.py: the 18.0 account.move ewaybill transport |
4 | | -# fields (preserved as openupgrade_legacy_19_0_* columns) become one |
5 | | -# l10n.in.ewaybill row per move in 19.0, linked via account_move_id. Rows |
6 | | -# enter state='pending' (the 19.0 default) -- 18.0 tracked only transport |
7 | | -# details on the move, never an e-Waybill number, so nothing was generated. |
8 | | -# company_id is a stored computed field (compute from account_move_id); set |
9 | | -# it explicitly from the move so the row isn't left NULL until a recompute. |
| 7 | +_logger = logging.getLogger(__name__) |
10 | 8 |
|
| 9 | +# Companion to pre-migration.py: the 18.0 account.move ewaybill data becomes |
| 10 | +# one l10n.in.ewaybill row per move, linked via account_move_id. A move |
| 11 | +# carries data when a transport field is meaningfully set (Integer distance |
| 12 | +# is ORM-written as 0, never NULL; mode '0' means managed-by-transporter) or |
| 13 | +# when an in_ewaybill_1_03 EDI document exists: 18.0 generated real e-waybills |
| 14 | +# through account_edi, so those rows get state generated/cancel and their NIC |
| 15 | +# number + dates from the EDI response attachment instead of looking |
| 16 | +# never-generated (which would invite duplicate re-generation against NIC). |
11 | 17 |
|
12 | | -@openupgrade.migrate() |
13 | | -def migrate(env, version): |
14 | | - legacy_distance = openupgrade.get_legacy_name("l10n_in_distance") |
15 | | - env.cr.execute( |
16 | | - """ |
17 | | - SELECT column_name FROM information_schema.columns |
18 | | - WHERE table_name = 'account_move' AND column_name = %s |
19 | | - """, |
20 | | - (legacy_distance,), |
21 | | - ) |
22 | | - if not env.cr.fetchone(): |
23 | | - return |
| 18 | +_NIC_DATE_FORMATS = ("%d/%m/%Y %I:%M:%S %p", "%d/%m/%Y %H:%M:%S", "%d/%m/%Y") |
24 | 19 |
|
25 | | - legacy = { |
26 | | - new_name: openupgrade.get_legacy_name(old_name) |
27 | | - for new_name, old_name in ( |
28 | | - ("distance", "l10n_in_distance"), |
29 | | - ("mode", "l10n_in_mode"), |
30 | | - ("transportation_doc_date", "l10n_in_transportation_doc_date"), |
31 | | - ("transportation_doc_no", "l10n_in_transportation_doc_no"), |
32 | | - ("transporter_id", "l10n_in_transporter_id"), |
33 | | - ("type_id", "l10n_in_type_id"), |
34 | | - ("vehicle_no", "l10n_in_vehicle_no"), |
35 | | - ("vehicle_type", "l10n_in_vehicle_type"), |
| 20 | + |
| 21 | +def _nic_date(value): |
| 22 | + for fmt in _NIC_DATE_FORMATS: |
| 23 | + try: |
| 24 | + return datetime.strptime(str(value), fmt).date() |
| 25 | + except (TypeError, ValueError): |
| 26 | + continue |
| 27 | + return None |
| 28 | + |
| 29 | + |
| 30 | +def _insert_ewaybills(env, legacy): |
| 31 | + data_preds = [ |
| 32 | + f"COALESCE(am.{legacy['distance']}, 0) <> 0", |
| 33 | + f"COALESCE(am.{legacy['mode']}, '0') <> '0'", |
| 34 | + ] + [ |
| 35 | + f"am.{legacy[c]} IS NOT NULL" |
| 36 | + for c in ( |
| 37 | + "transportation_doc_date", |
| 38 | + "transportation_doc_no", |
| 39 | + "transporter_id", |
| 40 | + "type_id", |
| 41 | + "vehicle_no", |
| 42 | + "vehicle_type", |
36 | 43 | ) |
37 | | - } |
38 | | - any_nonnull = " OR ".join(f"am.{c} IS NOT NULL" for c in legacy.values()) |
| 44 | + ] |
| 45 | + any_data = " OR ".join(data_preds) |
| 46 | + if openupgrade.table_exists(env.cr, "account_edi_document"): |
| 47 | + any_data += """ |
| 48 | + OR am.id IN ( |
| 49 | + SELECT d.move_id |
| 50 | + FROM account_edi_document d |
| 51 | + JOIN account_edi_format f ON f.id = d.edi_format_id |
| 52 | + WHERE f.code = 'in_ewaybill_1_03' |
| 53 | + AND d.state IN ('sent', 'to_cancel', 'cancelled') |
| 54 | + )""" |
39 | 55 |
|
40 | 56 | openupgrade.logged_query( |
41 | 57 | env.cr, |
@@ -65,6 +81,90 @@ def migrate(env, version): |
65 | 81 | COALESCE(am.write_uid, am.create_uid, 1), |
66 | 82 | COALESCE(am.write_date, am.create_date, NOW() AT TIME ZONE 'UTC') |
67 | 83 | FROM account_move am |
68 | | - WHERE {any_nonnull} |
| 84 | + WHERE {any_data} |
| 85 | + """, |
| 86 | + ) |
| 87 | + |
| 88 | + |
| 89 | +def _mark_generated_from_edi(env): |
| 90 | + """E-waybills 18.0 actually generated: state + NIC number/dates from the |
| 91 | + EDI response JSON (the attachment stores the response's data object).""" |
| 92 | + if not openupgrade.table_exists(env.cr, "account_edi_document"): |
| 93 | + return |
| 94 | + env.cr.execute( |
| 95 | + """ |
| 96 | + SELECT d.move_id, d.state, d.attachment_id |
| 97 | + FROM account_edi_document d |
| 98 | + JOIN account_edi_format f ON f.id = d.edi_format_id |
| 99 | + WHERE f.code = 'in_ewaybill_1_03' |
| 100 | + AND d.state IN ('sent', 'to_cancel', 'cancelled') |
| 101 | + """ |
| 102 | + ) |
| 103 | + Ewaybill = env["l10n.in.ewaybill"].with_context(tracking_disable=True) |
| 104 | + for move_id, edi_state, att_id in env.cr.fetchall(): |
| 105 | + ewaybill = Ewaybill.search([("account_move_id", "=", move_id)], limit=1) |
| 106 | + if not ewaybill: |
| 107 | + continue |
| 108 | + vals = {"state": "cancel" if edi_state == "cancelled" else "generated"} |
| 109 | + response = {} |
| 110 | + if att_id: |
| 111 | + raw = env["ir.attachment"].browse(att_id).raw |
| 112 | + try: |
| 113 | + response = json.loads(raw.decode("utf-8")) |
| 114 | + except (ValueError, UnicodeDecodeError, AttributeError): |
| 115 | + _logger.info("unparsable ewaybill response for move %s", move_id) |
| 116 | + if response.get("ewayBillNo"): |
| 117 | + vals["name"] = str(response["ewayBillNo"]) |
| 118 | + ewaybill_date = _nic_date(response.get("ewayBillDate")) |
| 119 | + if ewaybill_date: |
| 120 | + vals["ewaybill_date"] = ewaybill_date |
| 121 | + expiry = _nic_date(response.get("validUpto")) |
| 122 | + if expiry: |
| 123 | + vals["ewaybill_expiry_date"] = expiry |
| 124 | + ewaybill.write(vals) |
| 125 | + |
| 126 | + |
| 127 | +def _backfill_computed_and_feature(env): |
| 128 | + ewaybills = env["l10n.in.ewaybill"].search([("account_move_id", "!=", False)]) |
| 129 | + for fname in ( |
| 130 | + "partner_bill_from_id", |
| 131 | + "partner_bill_to_id", |
| 132 | + "partner_ship_from_id", |
| 133 | + "partner_ship_to_id", |
| 134 | + ): |
| 135 | + env.add_to_compute(ewaybills._fields[fname], ewaybills) |
| 136 | + ewaybills.env.flush_all() |
| 137 | + # the UI gate: enable for companies that own migrated e-waybills |
| 138 | + openupgrade.logged_query( |
| 139 | + env.cr, |
| 140 | + """ |
| 141 | + UPDATE res_company c |
| 142 | + SET l10n_in_ewaybill_feature = TRUE |
| 143 | + WHERE EXISTS ( |
| 144 | + SELECT 1 FROM l10n_in_ewaybill e WHERE e.company_id = c.id |
| 145 | + ) |
69 | 146 | """, |
70 | 147 | ) |
| 148 | + |
| 149 | + |
| 150 | +@openupgrade.migrate() |
| 151 | +def migrate(env, version): |
| 152 | + legacy_distance = openupgrade.get_legacy_name("l10n_in_distance") |
| 153 | + if not openupgrade.column_exists(env.cr, "account_move", legacy_distance): |
| 154 | + return |
| 155 | + legacy = { |
| 156 | + new_name: openupgrade.get_legacy_name(old_name) |
| 157 | + for new_name, old_name in ( |
| 158 | + ("distance", "l10n_in_distance"), |
| 159 | + ("mode", "l10n_in_mode"), |
| 160 | + ("transportation_doc_date", "l10n_in_transportation_doc_date"), |
| 161 | + ("transportation_doc_no", "l10n_in_transportation_doc_no"), |
| 162 | + ("transporter_id", "l10n_in_transporter_id"), |
| 163 | + ("type_id", "l10n_in_type_id"), |
| 164 | + ("vehicle_no", "l10n_in_vehicle_no"), |
| 165 | + ("vehicle_type", "l10n_in_vehicle_type"), |
| 166 | + ) |
| 167 | + } |
| 168 | + _insert_ewaybills(env, legacy) |
| 169 | + _mark_generated_from_edi(env) |
| 170 | + _backfill_computed_and_feature(env) |
0 commit comments