Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docsource/modules180-190.rst
Original file line number Diff line number Diff line change
Expand Up @@ -452,7 +452,7 @@ Module coverage 18.0 -> 19.0
+---------------------------------------------------+----------------------+-------------------------------------------------+
| |del| l10n_in_edi_ewaybill | | |
+---------------------------------------------------+----------------------+-------------------------------------------------+
| |new| l10n_in_ewaybill | | |
| |new| l10n_in_ewaybill | Done | |
+---------------------------------------------------+----------------------+-------------------------------------------------+
| |new| l10n_in_ewaybill_irn | | |
+---------------------------------------------------+----------------------+-------------------------------------------------+
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
import json
import logging
from datetime import datetime

from openupgradelib import openupgrade

_logger = logging.getLogger(__name__)

# Companion to pre-migration.py: the 18.0 account.move ewaybill data becomes
# one l10n.in.ewaybill row per move, linked via account_move_id. A move
# carries data when a transport field is meaningfully set (Integer distance
# is ORM-written as 0, never NULL; mode '0' means managed-by-transporter) or
# when an in_ewaybill_1_03 EDI document exists: 18.0 generated real e-waybills
# through account_edi, so those rows get state generated/cancel and their NIC
# number + dates from the EDI response attachment instead of looking
# never-generated (which would invite duplicate re-generation against NIC).

_NIC_DATE_FORMATS = ("%d/%m/%Y %I:%M:%S %p", "%d/%m/%Y %H:%M:%S", "%d/%m/%Y")


def _nic_date(value):
for fmt in _NIC_DATE_FORMATS:
try:
return datetime.strptime(str(value), fmt).date()
except (TypeError, ValueError):
continue
return None


def _insert_ewaybills(env, legacy):
data_preds = [
f"COALESCE(am.{legacy['distance']}, 0) <> 0",
f"COALESCE(am.{legacy['mode']}, '0') <> '0'",
] + [
f"am.{legacy[c]} IS NOT NULL"
for c in (
"transportation_doc_date",
"transportation_doc_no",
"transporter_id",
"type_id",
"vehicle_no",
"vehicle_type",
)
]
any_data = " OR ".join(data_preds)
if openupgrade.table_exists(env.cr, "account_edi_document"):
any_data += """
OR am.id IN (
SELECT d.move_id
FROM account_edi_document d
JOIN account_edi_format f ON f.id = d.edi_format_id
WHERE f.code = 'in_ewaybill_1_03'
AND d.state IN ('sent', 'to_cancel', 'cancelled')
)"""

openupgrade.logged_query(
env.cr,
f"""
INSERT INTO l10n_in_ewaybill (
account_move_id, company_id, state,
distance, mode,
transportation_doc_date, transportation_doc_no,
transporter_id, type_id,
vehicle_no, vehicle_type,
create_uid, create_date, write_uid, write_date
)
SELECT
am.id,
am.company_id,
'pending'::varchar,
am.{legacy["distance"]},
CASE WHEN am.{legacy["mode"]} = '0' THEN NULL ELSE am.{legacy["mode"]} END,
am.{legacy["transportation_doc_date"]},
am.{legacy["transportation_doc_no"]},
am.{legacy["transporter_id"]},
am.{legacy["type_id"]},
am.{legacy["vehicle_no"]},
am.{legacy["vehicle_type"]},
COALESCE(am.write_uid, am.create_uid, 1),
COALESCE(am.write_date, am.create_date, NOW() AT TIME ZONE 'UTC'),
COALESCE(am.write_uid, am.create_uid, 1),
COALESCE(am.write_date, am.create_date, NOW() AT TIME ZONE 'UTC')
FROM account_move am
WHERE {any_data}
""",
)


def _mark_generated_from_edi(env):
"""E-waybills 18.0 actually generated: state + NIC number/dates from the
EDI response JSON (the attachment stores the response's data object)."""
if not openupgrade.table_exists(env.cr, "account_edi_document"):
return
env.cr.execute(
"""
SELECT d.move_id, d.state, d.attachment_id
FROM account_edi_document d
JOIN account_edi_format f ON f.id = d.edi_format_id
WHERE f.code = 'in_ewaybill_1_03'
AND d.state IN ('sent', 'to_cancel', 'cancelled')
"""
)
Ewaybill = env["l10n.in.ewaybill"].with_context(tracking_disable=True)
for move_id, edi_state, att_id in env.cr.fetchall():
ewaybill = Ewaybill.search([("account_move_id", "=", move_id)], limit=1)
if not ewaybill:
continue
vals = {"state": "cancel" if edi_state == "cancelled" else "generated"}
response = {}
if att_id:
raw = env["ir.attachment"].browse(att_id).raw
try:
response = json.loads(raw.decode("utf-8"))
except (ValueError, UnicodeDecodeError, AttributeError):
_logger.info("unparsable ewaybill response for move %s", move_id)
if response.get("ewayBillNo"):
vals["name"] = str(response["ewayBillNo"])
ewaybill_date = _nic_date(response.get("ewayBillDate"))
if ewaybill_date:
vals["ewaybill_date"] = ewaybill_date
expiry = _nic_date(response.get("validUpto"))
if expiry:
vals["ewaybill_expiry_date"] = expiry
ewaybill.write(vals)


def _backfill_computed_and_feature(env):
ewaybills = env["l10n.in.ewaybill"].search([("account_move_id", "!=", False)])
for fname in (
"partner_bill_from_id",
"partner_bill_to_id",
"partner_ship_from_id",
"partner_ship_to_id",
):
env.add_to_compute(ewaybills._fields[fname], ewaybills)
ewaybills.env.flush_all()
# the UI gate: enable for companies that own migrated e-waybills
openupgrade.logged_query(
env.cr,
"""
UPDATE res_company c
SET l10n_in_ewaybill_feature = TRUE
WHERE EXISTS (
SELECT 1 FROM l10n_in_ewaybill e WHERE e.company_id = c.id
)
""",
)


@openupgrade.migrate()
def migrate(env, version):
legacy_distance = openupgrade.get_legacy_name("l10n_in_distance")
if not openupgrade.column_exists(env.cr, "account_move", legacy_distance):
return
legacy = {
new_name: openupgrade.get_legacy_name(old_name)
for new_name, old_name in (
("distance", "l10n_in_distance"),
("mode", "l10n_in_mode"),
("transportation_doc_date", "l10n_in_transportation_doc_date"),
("transportation_doc_no", "l10n_in_transportation_doc_no"),
("transporter_id", "l10n_in_transporter_id"),
("type_id", "l10n_in_type_id"),
("vehicle_no", "l10n_in_vehicle_no"),
("vehicle_type", "l10n_in_vehicle_type"),
)
}
_insert_ewaybills(env, legacy)
_mark_generated_from_edi(env)
_backfill_computed_and_feature(env)
104 changes: 104 additions & 0 deletions openupgrade_scripts/scripts/l10n_in_ewaybill/19.0.2.0/pre-migration.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
from openupgradelib import openupgrade

# l10n_in_ewaybill 19.0 converges several changes (see PR body for detail):
# the l10n.in.ewaybill[.cancel] models move out of l10n_in_ewaybill_stock,
# six xmlids move with them, three res.company credential columns drop their
# `_edi_` segment, and eight account.move ewaybill columns are preserved as
# legacy for post-migration.py to spawn l10n.in.ewaybill rows. The
# l10n_in_edi_ewaybill -> l10n_in_ewaybill module rename is already handled by
# apriori (update_module_names in base pre-migration), so it needs no work here.

_renamed_xmlids_from_stock = [
(
"l10n_in_ewaybill_stock.l10n_in_ewaybill_form_action",
"l10n_in_ewaybill.l10n_in_ewaybill_form_action",
),
(
"l10n_in_ewaybill_stock.action_report_ewaybill",
"l10n_in_ewaybill.action_report_ewaybill",
),
(
"l10n_in_ewaybill_stock.access_l10n_in_ewaybill",
"l10n_in_ewaybill.access_l10n_in_ewaybill",
),
(
"l10n_in_ewaybill_stock.access_l10n_in_ewaybill_cancel",
"l10n_in_ewaybill.access_l10n_in_ewaybill_cancel",
),
(
"l10n_in_ewaybill_stock.l10n_in_ewaybill_comp_rule",
"l10n_in_ewaybill.l10n_in_ewaybill_comp_rule",
),
(
"l10n_in_ewaybill_stock.paperformat_ewaybill",
"l10n_in_ewaybill.paperformat_ewaybill",
),
]

_preserved_columns_account_move = {
# 18.0 columns owned by l10n_in_edi_ewaybill; DEL in 19.0. We preserve
# them as openupgrade_legacy_19_0_* so post-migration.py can spawn
# l10n.in.ewaybill rows from them.
"account_move": [
("l10n_in_distance", None),
("l10n_in_mode", None),
("l10n_in_transportation_doc_date", None),
("l10n_in_transportation_doc_no", None),
("l10n_in_transporter_id", None),
("l10n_in_type_id", None),
("l10n_in_vehicle_no", None),
("l10n_in_vehicle_type", None),
],
}

_renamed_fields_company_credentials = [
(
"res.company",
"res_company",
"l10n_in_edi_ewaybill_auth_validity",
"l10n_in_ewaybill_auth_validity",
),
(
"res.company",
"res_company",
"l10n_in_edi_ewaybill_password",
"l10n_in_ewaybill_password",
),
(
"res.company",
"res_company",
"l10n_in_edi_ewaybill_username",
"l10n_in_ewaybill_username",
),
]


@openupgrade.migrate()
def migrate(env, version):
# Move the model ownership: ir.model + ir.model.fields rows that point
# at l10n.in.ewaybill / l10n.in.ewaybill.cancel get their `module`
# column rewritten from l10n_in_ewaybill_stock to l10n_in_ewaybill.
# The SQL table itself is not renamed (the table name is identical).
openupgrade.update_module_moved_models(
env.cr, "l10n.in.ewaybill", "l10n_in_ewaybill_stock", "l10n_in_ewaybill"
)
openupgrade.update_module_moved_models(
env.cr, "l10n.in.ewaybill.cancel", "l10n_in_ewaybill_stock", "l10n_in_ewaybill"
)

# Six XML records (action, report, two access rules, rule, paperformat)
# ship under l10n_in_ewaybill in 19.0 but lived under
# l10n_in_ewaybill_stock in 18.0. Rename so customer references to
# them survive the upgrade.
openupgrade.rename_xmlids(env.cr, _renamed_xmlids_from_stock)

# Three res.company credential columns drop the `_edi_` segment.
# apriori's update_module_names already moved the ir_model_fields
# ownership; rename_fields handles the SQL column rename + filter /
# export / translation side effects.
openupgrade.rename_fields(env, _renamed_fields_company_credentials)

# Preserve the 8 account.move ewaybill columns before Odoo's update_db
# drops them. post-migration.py consumes the legacy values to spawn
# l10n.in.ewaybill rows.
openupgrade.rename_columns(env.cr, _preserved_columns_account_move)
Loading
Loading