This commit is contained in:
gsinghpal
2026-05-21 03:37:25 -04:00
parent b2f483d67c
commit 1314f4581d
47 changed files with 5730 additions and 177 deletions

View File

@@ -0,0 +1,135 @@
# -*- coding: utf-8 -*-
"""One-off rate-quote sweep across every FedEx service code.
Loops the full carrier selection (~38 services) against two routes —
CA domestic (matching SO-30045) and CA → US — to figure out which
services are valid for the shipping lanes EN Technologies actually
uses. Prints a CSV-ish matrix to stdout so the report can be pasted
straight into chat.
Run with:
odoo shell -c /etc/odoo/odoo.conf -d admin --no-http < this_file
"""
from types import SimpleNamespace
from odoo.addons.fusion_shipping.api.fedex_rest.request import (
FedexRequest as FedexRestRequest,
)
CARRIER_ID = 17 # FedEx REST carrier on entech
WEIGHT_LB = 5.0
DIMS = {'length': 12, 'width': 10, 'height': 6}
carrier = env['delivery.carrier'].browse(CARRIER_ID)
assert carrier.exists(), 'FedEx carrier id=17 not found on this DB.'
service_codes = [code for code, _label in carrier._fields[
'fedex_rest_service_type'
].selection]
# CA Toronto sender (from company address)
sender = SimpleNamespace(
street='36 Taber Road', street2=False,
city='Toronto', zip='M9W3A8',
state_id=env['res.country.state'].search(
[('code', '=', 'ON'), ('country_id.code', '=', 'CA')], limit=1,
),
country_id=env['res.country'].search([('code', '=', 'CA')], limit=1),
name='ENTECH', phone='4167492400', email='ship@entech.test',
commercial_partner_id=None, parent_id=None, vat=False,
is_company=True,
)
# Route A — CA domestic (Niagara Falls, ON)
ca_recipient = env['res.partner'].search([
('city', 'ilike', 'Niagara Falls'), ('country_id.code', '=', 'CA'),
], limit=1)
assert ca_recipient.exists(), 'No CA partner found for the domestic route.'
# Route B — CA → US (a real US partner with a complete address)
us_recipient = env['res.partner'].search([
('country_id.code', '=', 'US'), ('city', '!=', False),
('zip', '!=', False), ('state_id', '!=', False),
], limit=1)
if not us_recipient:
# Fabricate one in memory (we won't write to DB).
us_recipient = env['res.partner'].new({
'name': 'Test US Customer',
'street': '1 World Trade Center',
'city': 'New York',
'zip': '10007',
'state_id': env['res.country.state'].search(
[('code', '=', 'NY'), ('country_id.code', '=', 'US')], limit=1,
).id,
'country_id': env['res.country'].search(
[('code', '=', 'US')], limit=1,
).id,
'phone': '2125551212',
'email': 'us@test.com',
})
# Sender partner — use the company partner for proper address resolution.
sender_partner = env.company.partner_id
def quote(service_code, recipient):
srm = FedexRestRequest(carrier)
srm.service_type = service_code
pkg = SimpleNamespace(
weight=WEIGHT_LB,
dimension=DIMS,
packaging_type='YOUR_PACKAGING',
total_cost=0, commodities=[], currency_id=env.ref('base.CAD'),
)
try:
res = srm._get_shipping_price(
ship_from=sender_partner,
ship_to=recipient,
packages=[pkg],
currency='CAD',
)
return {
'ok': True,
'price': res.get('price'),
'currency': res.get('currency'),
'service_name': res.get('service_name', '').strip(),
'delivery': (res.get('delivery_timestamp') or '')[:16].replace(
'T', ' ',
),
'transit': res.get('transit_time', ''),
'error': '',
}
except Exception as exc:
msg = str(exc).replace('\n', ' ').strip()
# Trim Odoo's "Error from FedEx: " prefix if present.
return {
'ok': False, 'price': 0, 'currency': '',
'service_name': '', 'delivery': '', 'transit': '',
'error': msg[:140],
}
def emit_row(route, code, label, result):
print('|{route}|{code}|{label}|{ok}|{price}|{cur}|{eta}|{transit}|{err}|'.format(
route=route,
code=code,
label=label[:50],
ok='OK' if result['ok'] else 'FAIL',
price=('%.2f' % result['price']) if result['ok'] else '',
cur=result['currency'],
eta=result['delivery'],
transit=result['transit'],
err=result['error'],
))
print('|Route|ServiceCode|Label|Status|Price|Cur|DeliveryETA|Transit|Error|')
print('|---|---|---|---|---|---|---|---|---|')
label_map = dict(carrier._fields['fedex_rest_service_type'].selection)
for code in service_codes:
label = label_map.get(code, code)
emit_row('CA->CA', code, label, quote(code, ca_recipient))
emit_row('CA->US', code, label, quote(code, us_recipient))
print('DONE')

View File

@@ -0,0 +1,56 @@
# -*- coding: utf-8 -*-
"""One-off: reset CoC-30045 to draft for re-issue testing.
Clears all thickness data + attachments so the operator can re-run
the Issue Certs wizard from a clean slate (upload RTF + PNG image,
verify the full inline-render flow).
Run with:
odoo shell -c /etc/odoo/odoo.conf -d admin --no-http < this_file
"""
cert = env['fp.certificate'].browse(501)
print('before: state=%s, readings=%d, attachment_id=%s, evidence=%s, image=%s' % (
cert.state,
len(cert.thickness_reading_ids),
cert.attachment_id.id if cert.attachment_id else None,
cert.x_fc_local_thickness_evidence_id.id if cert.x_fc_local_thickness_evidence_id else None,
cert.x_fc_thickness_image_id.id if cert.x_fc_thickness_image_id else None,
))
# Drop all readings
n_readings = len(cert.thickness_reading_ids)
cert.thickness_reading_ids.unlink()
# Delete any attachments on the cert (RTF, regenerated PDF, image)
atts = env['ir.attachment'].search([
('res_model', '=', 'fp.certificate'),
('res_id', '=', cert.id),
])
n_atts = len(atts)
atts.unlink()
# Wipe cert-level thickness metadata + state-affecting fields
cert.write({
'state': 'draft',
'attachment_id': False,
'x_fc_local_thickness_pdf': False,
'x_fc_local_thickness_pdf_filename': False,
'x_fc_local_thickness_evidence_id': False,
'x_fc_thickness_image_id': False,
'x_fc_thickness_operator': False,
'x_fc_thickness_product': False,
'x_fc_thickness_application': False,
'x_fc_thickness_directory': False,
'x_fc_thickness_equipment': False,
'x_fc_thickness_datetime': False,
'x_fc_thickness_measuring_time_sec': 0,
'x_fc_thickness_source_filename': False,
})
env.cr.commit()
cert.invalidate_recordset()
print('after: state=%s, readings=%d, attachments_removed=%d, prior_readings=%d' % (
cert.state, len(cert.thickness_reading_ids), n_atts, n_readings,
))
print('CoC-30045 ready for re-issue. Open the Issue Certs wizard '
'from WO-30045, upload the RTF + PNG image, click Confirm & Issue.')

View File

@@ -0,0 +1,52 @@
# -*- coding: utf-8 -*-
"""Retro-extract WMF image from CoC-30045's attached RTF, attach as
PNG, link to x_fc_thickness_image_id, regenerate cert PDF.
Run with:
odoo shell -c /etc/odoo/odoo.conf -d admin --no-http < this_file
"""
import base64
from odoo.addons.fusion_plating_jobs.wizards.fp_cert_issue_wizard import (
_fp_extract_rtf_images,
_fp_pick_microscope_image,
)
cert = env['fp.certificate'].browse(501)
att = env['ir.attachment'].search([
('res_model', '=', 'fp.certificate'),
('res_id', '=', 501),
('name', 'ilike', 'XRF'),
], limit=1)
raw = base64.b64decode(att.datas)
pngs = _fp_extract_rtf_images(raw)
print('extracted PNG blocks:', len(pngs),
'sizes:', [len(p) for p in pngs])
img_bytes, w, h = _fp_pick_microscope_image(pngs)
if not img_bytes:
print('no microscope image found (all blocks below area threshold)')
else:
print('picked microscope image: %dx%d, %d bytes' % (w, h, len(img_bytes)))
img_att = env['ir.attachment'].sudo().create({
'name': 'CoC-30045-microscope.png',
'type': 'binary',
'datas': base64.b64encode(img_bytes),
'mimetype': 'image/png',
'res_model': 'fp.certificate',
'res_id': cert.id,
})
cert.write({'x_fc_thickness_image_id': img_att.id})
print('attached as ir.attachment id=%d' % img_att.id)
# Regenerate the cert PDF so the layout includes the image.
if cert.attachment_id:
cert.attachment_id.unlink()
cert.invalidate_recordset()
new_att = cert._fp_render_and_attach_pdf()
env.cr.commit()
print('regen done · cert PDF=%s · size=%d bytes' % (
new_att.name if new_att else 'NONE',
new_att.file_size if new_att else 0,
))

View File

@@ -0,0 +1,77 @@
# -*- coding: utf-8 -*-
"""One-off: re-parse RTF on CoC-30045 + populate new metadata fields
+ regenerate the cert PDF. Run on entech after deploying the parser
extensions.
Run with:
odoo shell -c /etc/odoo/odoo.conf -d admin --no-http < this_file
"""
import base64
from datetime import datetime
from odoo.addons.fusion_plating_jobs.wizards.fp_cert_issue_wizard import (
_fp_parse_fischerscope_rtf,
)
cert = env['fp.certificate'].browse(501)
att = env['ir.attachment'].search([
('res_model', '=', 'fp.certificate'),
('res_id', '=', 501),
('name', 'ilike', 'XRF'),
], limit=1)
raw = base64.b64decode(att.datas)
parsed = _fp_parse_fischerscope_rtf(raw)
print('parsed metadata:', {
k: parsed[k] for k in (
'operator', 'product', 'application', 'directory',
'equipment', 'measuring_time_sec', 'date_str', 'time_str',
'calibration',
)
})
vals = {
'x_fc_thickness_operator': parsed['operator'],
'x_fc_thickness_product': parsed['product'],
'x_fc_thickness_application': parsed['application'],
'x_fc_thickness_directory': parsed['directory'],
'x_fc_thickness_equipment': parsed['equipment'] or 'Fischerscope XDAL 600',
'x_fc_thickness_measuring_time_sec': parsed['measuring_time_sec'] or 0,
'x_fc_thickness_source_filename': att.name,
}
date_str = (parsed.get('date_str') or '').strip()
time_str = (parsed.get('time_str') or '').strip()
if date_str:
combined = ('%s %s' % (date_str, time_str)).strip()
for fmt in (
'%m/%d/%Y %I:%M:%S %p', '%m/%d/%Y %I:%M %p',
'%m/%d/%Y %H:%M:%S', '%m/%d/%Y %H:%M',
'%m/%d/%Y',
):
try:
vals['x_fc_thickness_datetime'] = datetime.strptime(combined, fmt)
break
except ValueError:
continue
cert.write(vals)
print('wrote vals:', list(vals.keys()))
# Backfill calibration on existing readings (created earlier).
calib = parsed.get('calibration') or ''
if calib:
cert.thickness_reading_ids.write({'calibration_std_ref': calib})
print('backfilled calibration on %d readings' % len(cert.thickness_reading_ids))
# Regenerate the cert PDF so the new layout takes effect.
if cert.attachment_id:
cert.attachment_id.unlink()
cert.invalidate_recordset()
new_att = cert._fp_render_and_attach_pdf()
env.cr.commit()
print('done · readings=%d · new PDF=%s · size=%d bytes' % (
len(cert.thickness_reading_ids),
new_att.name if new_att else 'NONE',
new_att.file_size if new_att else 0,
))