summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAzka Nathan <darizkyfaz@gmail.com>2025-05-27 09:03:37 +0700
committerAzka Nathan <darizkyfaz@gmail.com>2025-05-27 09:03:37 +0700
commitf7c4899b84ca0faa3c069734174829898f20a420 (patch)
treec9f1ac31026fabcc5c1108b128dc5459c30fb6d2
parent8ac8c2f8b89e6069bf13e879d486fa9cf4814cb4 (diff)
parent2f30191860095f3f2b403bdece99b4e46e7d4ff2 (diff)
Merge branch 'odoo-backup' of bitbucket.org:altafixco/indoteknik-addons into odoo-backup
-rw-r--r--indoteknik_api/controllers/api_v1/sale_order.py24
-rw-r--r--indoteknik_custom/models/dunning_run.py5
-rwxr-xr-xindoteknik_custom/models/product_template.py160
-rwxr-xr-xindoteknik_custom/models/sale_order.py34
-rw-r--r--indoteknik_custom/models/user_pengajuan_tempo_request.py3
-rwxr-xr-xindoteknik_custom/views/sale_order.xml1
6 files changed, 131 insertions, 96 deletions
diff --git a/indoteknik_api/controllers/api_v1/sale_order.py b/indoteknik_api/controllers/api_v1/sale_order.py
index 98b13cad..e8c2c75a 100644
--- a/indoteknik_api/controllers/api_v1/sale_order.py
+++ b/indoteknik_api/controllers/api_v1/sale_order.py
@@ -54,6 +54,20 @@ class SaleOrder(controller.Controller):
# sales = request.env['sale.order'].search_read([('name', '=', sale_number)], fields=['id', 'name', 'amount_total', 'state'])
sales = request.env['sale.order'].search(query, limit=1)
data = []
+ INDONESIAN_MONTHS = {
+ 1: 'Januari',
+ 2: 'Februari',
+ 3: 'Maret',
+ 4: 'April',
+ 5: 'Mei',
+ 6: 'Juni',
+ 7: 'Juli',
+ 8: 'Agustus',
+ 9: 'September',
+ 10: 'Oktober',
+ 11: 'November',
+ 12: 'Desember',
+ }
for sale in sales:
product_name = ''
product_not_in_id = 0
@@ -69,6 +83,7 @@ class SaleOrder(controller.Controller):
'amount_untaxed': sale.amount_untaxed,
'amount_tax': sale.amount_tax,
'amount_total': sale.amount_total,
+ 'expected_ready_to_ship': f"{sale.expected_ready_to_ship.day} {INDONESIAN_MONTHS[sale.expected_ready_to_ship.month]} {sale.expected_ready_to_ship.year}",
'product_name': product_name,
'product_not_in_id': product_not_in_id,
'details': [request.env['sale.order.line'].api_single_response(x, context='with_detail') for x in sale.order_line]
@@ -142,6 +157,15 @@ class SaleOrder(controller.Controller):
sale_order = request.env['sale.order'].search(domain)
if sale_order:
data = request.env['sale.order'].api_v1_single_response(sale_order, context='with_detail')
+ if sale_order.expected_ready_to_ship:
+ bulan_id = [
+ "Januari", "Februari", "Maret", "April", "Mei", "Juni",
+ "Juli", "Agustus", "September", "Oktober", "November", "Desember"
+ ]
+ tanggal = sale_order.expected_ready_to_ship.day
+ bulan = bulan_id[sale_order.expected_ready_to_ship.month - 1]
+ tahun = sale_order.expected_ready_to_ship.year
+ data['expected_ready_to_ship'] = f"{tanggal} {bulan} {tahun}"
return self.response(data)
diff --git a/indoteknik_custom/models/dunning_run.py b/indoteknik_custom/models/dunning_run.py
index c167aab7..bb53fc0c 100644
--- a/indoteknik_custom/models/dunning_run.py
+++ b/indoteknik_custom/models/dunning_run.py
@@ -19,7 +19,7 @@ class DunningRun(models.Model):
partner_id = fields.Many2one(
'res.partner', string='Customer',
required=True, change_default=True, index=True, tracking=1)
- dunning_line = fields.One2many('dunning.run.line', 'dunning_id', string='Dunning Lines', auto_join=True)
+ dunning_line = fields.One2many('dunning.run.line', 'dunning_id', string='Dunning Lines', auto_join=True, order='invoice_id desc')
# dunning_level = fields.Integer(string='Dunning Level', default=30, help='30 hari sebelum jatuh tempo invoice')
date_kirim_tukar_faktur = fields.Date(string='Kirim Faktur')
resi_tukar_faktur = fields.Char(string='Resi Faktur')
@@ -122,7 +122,8 @@ class DunningRun(models.Model):
class DunningRunLine(models.Model):
_name = 'dunning.run.line'
_description = 'Dunning Run Line'
- _order = 'dunning_id, id'
+ # _order = 'dunning_id, id'
+ _order = 'invoice_id desc, id'
dunning_id = fields.Many2one('dunning.run', string='Dunning Ref', required=True, ondelete='cascade', index=True, copy=False)
partner_id = fields.Many2one('res.partner', string='Customer')
diff --git a/indoteknik_custom/models/product_template.py b/indoteknik_custom/models/product_template.py
index c62ca63e..3bb54f44 100755
--- a/indoteknik_custom/models/product_template.py
+++ b/indoteknik_custom/models/product_template.py
@@ -797,7 +797,7 @@ class ProductTemplate(models.Model):
self.message_post(body=f"<b>Vendor Pricelist</b>: all removed")
def _log_field_changes_product(self, vals, old_values):
- """Log general field changes for product template"""
+ """Revised - Log general field changes for product template without posting to variants"""
exclude_fields = ['solr_flag', 'desc_update_solr', 'last_update_solr', 'is_edited']
image_fields = ['image_1920', 'image_carousel_lines', 'product_template_image_ids']
@@ -867,13 +867,9 @@ class ProductTemplate(models.Model):
changes.append(f"<li><b>{field_label}</b>: '{old_val_str}' → '{new_val_str}'</li>")
if changes:
+ # PERBAIKAN: Hanya post ke template, HAPUS bagian log ke variants
record.message_post(body=f"<b>Updated:</b><ul>{''.join(changes)}</ul>")
- # log changes to product variants
- variant_message = f"<b>Updated:</b><ul>{''.join(changes)}</ul>"
- for variant in record.product_variant_ids:
- variant.message_post(body=variant_message)
-
# simpan data lama dan log perubahan field
def write(self, vals):
context = self._get_context_with_all_info(vals)
@@ -1235,101 +1231,77 @@ class ProductProduct(models.Model):
# log perubahan field
def _log_field_changes_product_variants(self, vals, old_values):
- exclude_fields = ['solr_flag', 'desc_update_solr', 'last_update_solr', 'is_edited']
- # for image fields, use custom labels
- custom_labels = {
- 'image_1920': 'Main Image',
- 'image_carousel_lines': 'Carousel Images',
- 'product_template_image_ids': 'Extra Product Media',
- }
-
- template_changes = {}
+ """Revised - Log field changes for variants without posting to template"""
+ exclude_fields = ['solr_flag', 'desc_update_solr', 'last_update_solr', 'is_edited']
+
+ # Custom labels for image fields
+ custom_labels = {
+ 'image_1920': 'Main Image',
+ 'image_carousel_lines': 'Carousel Images',
+ 'product_template_image_ids': 'Extra Product Media',
+ }
- for record in self:
- changes = []
- for field_name in vals:
- if field_name not in record._fields or field_name in exclude_fields:
- continue
+ for record in self:
+ changes = []
+ for field_name in vals:
+ if field_name not in record._fields or field_name in exclude_fields:
+ continue
- field = record._fields[field_name]
- field_label = custom_labels.get(field_name, field.string or field_name)
- old_value = old_values.get(record.id, {}).get(field_name)
- new_value = record[field_name] # nilai setelah write
+ field = record._fields[field_name]
+ field_label = custom_labels.get(field_name, field.string or field_name)
+ old_value = old_values.get(record.id, {}).get(field_name)
+ new_value = record[field_name]
- def stringify(val, field, record):
- if val in [None, False]:
- return 'None'
- if isinstance(field, fields.Selection):
- selection = field.selection
- if callable(selection):
- selection = selection(record)
- return dict(selection).get(val, str(val))
- if isinstance(field, fields.Boolean):
- return 'Yes' if val else 'No'
- if isinstance(field, fields.Many2one):
- if isinstance(val, int):
- rec = record.env[field.comodel_name].browse(val)
- return rec.display_name if rec.exists() else str(val)
- elif isinstance(val, models.BaseModel):
- return val.display_name
- return str(val)
- if isinstance(field, fields.Many2many):
- records = val if isinstance(val, models.BaseModel) else record[field.name]
- if not records:
- return 'None'
- for attr in ['name', 'x_name', 'display_name']:
- if hasattr(records[0], attr):
- return ", ".join(records.mapped(attr))
- return ", ".join(str(r.id) for r in records)
- if isinstance(field, fields.One2many):
- records = val if isinstance(val, models.BaseModel) else record[field.name]
- if not records:
- return 'None'
- return f"{field.comodel_name}({', '.join(str(r.id) for r in records)})"
+ def stringify(val, field, record):
+ if val in [None, False]:
+ return 'None'
+ if isinstance(field, fields.Selection):
+ selection = field.selection
+ if callable(selection):
+ selection = selection(record)
+ return dict(selection).get(val, str(val))
+ if isinstance(field, fields.Boolean):
+ return 'Yes' if val else 'No'
+ if isinstance(field, fields.Many2one):
+ if isinstance(val, int):
+ rec = record.env[field.comodel_name].browse(val)
+ return rec.display_name if rec.exists() else str(val)
+ elif isinstance(val, models.BaseModel):
+ return val.display_name
return str(val)
+ if isinstance(field, fields.Many2many):
+ records = val if isinstance(val, models.BaseModel) else record[field.name]
+ if not records:
+ return 'None'
+ for attr in ['name', 'x_name', 'display_name']:
+ if hasattr(records[0], attr):
+ return ", ".join(records.mapped(attr))
+ return ", ".join(str(r.id) for r in records)
+ if isinstance(field, fields.One2many):
+ records = val if isinstance(val, models.BaseModel) else record[field.name]
+ if not records:
+ return 'None'
+ return f"{field.comodel_name}({', '.join(str(r.id) for r in records)})"
+ return str(val)
- old_val_str = stringify(old_value, field, record)
- new_val_str = stringify(new_value, field, record)
-
- if old_val_str != new_val_str:
- if field_name in custom_labels: # handle image field
- if old_val_str == 'None' and new_val_str != 'None':
- changes.append(f"<li><b>{field_label}</b>: image added</li>")
- elif old_val_str != 'None' and new_val_str == 'None':
- changes.append(f"<li><b>{field_label}</b>: image removed</li>")
- else:
- changes.append(f"<li><b>{field_label}</b>: image updated</li>")
- else:
- changes.append(f"<li><b>{field_label}</b>: '{old_val_str}' → '{new_val_str}'</li>")
+ old_val_str = stringify(old_value, field, record)
+ new_val_str = stringify(new_value, field, record)
- if changes:
- # Post message to variant
- variant_message = "<b>Updated:</b><ul>%s</ul>" % "".join(changes)
- record.message_post(body=variant_message)
-
- # Group changes by template for posting to template
- template_id = record.product_tmpl_id.id
- if template_id not in template_changes:
- template_changes[template_id] = {}
-
- # Store variant information including ID for creating clickable link
- template_changes[template_id][record.id] = {
- 'name': record.display_name or f"Variant {record.id}",
- 'changes': changes
- }
+ if old_val_str != new_val_str:
+ if field_name in custom_labels: # handle image field
+ if old_val_str == 'None' and new_val_str != 'None':
+ changes.append(f"<li><b>{field_label}</b>: image added</li>")
+ elif old_val_str != 'None' and new_val_str == 'None':
+ changes.append(f"<li><b>{field_label}</b>: image removed</li>")
+ else:
+ changes.append(f"<li><b>{field_label}</b>: image updated</li>")
+ else:
+ changes.append(f"<li><b>{field_label}</b>: '{old_val_str}' → '{new_val_str}'</li>")
- # Post grouped messages to templates with clickable links using your format
- for template_id, variants_data in template_changes.items():
- template = self.env['product.template'].browse(template_id)
- if template.exists():
- template_message = "<b>Variant Updates:</b><br/>"
-
- for variant_id, variant_data in variants_data.items():
- # Create clickable link using your format
- variant_link = f"<a href=\"#id={variant_id}&model=product.product&view_type=form\">{variant_data['name']}</a><br/>"
- template_message += f"{variant_link}<ul>{''.join(variant_data['changes'])}</ul><br/>"
-
- template.message_post(body=template_message)
+ if changes:
+ # PERBAIKAN: Hanya post message ke variant, HAPUS bagian template_changes
+ variant_message = "<b>Updated:</b><ul>%s</ul>" % "".join(changes)
+ record.message_post(body=variant_message)
# simpan data lama dan log perubahan field
def write(self, vals):
diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py
index bdf8f1eb..f89dfb10 100755
--- a/indoteknik_custom/models/sale_order.py
+++ b/indoteknik_custom/models/sale_order.py
@@ -331,6 +331,10 @@ class SaleOrder(models.Model):
('hold', 'Hold'),
('approve', 'Approve')
], tracking=True, string='State Cancel', copy=False)
+ ready_to_ship_status_detail = fields.Char(
+ string='Status Shipping Detail',
+ compute='_compute_ready_to_ship_status_detail'
+ )
date_hold = fields.Datetime(string='Date Hold', tracking=True, readonly=True, help='Waktu ketika SO di Hold'
)
date_unhold = fields.Datetime(string='Date Unhold', tracking=True, readonly=True, help='Waktu ketika SO di Unhold'
@@ -2008,3 +2012,33 @@ class SaleOrder(models.Model):
if any(field in vals for field in ["order_line", "client_order_ref"]):
self._calculate_etrts_date()
return res
+
+ # @api.depends('commitment_date')
+ def _compute_ready_to_ship_status_detail(self):
+ for order in self:
+ eta = order.commitment_date
+ match_lines = self.env['purchase.order.sales.match'].search([
+ ('sale_id', '=', order.id)
+ ])
+ if match_lines:
+ for match in match_lines:
+ po = match.purchase_order_id
+ product = match.product_id
+ po_line = self.env['purchase.order.line'].search([
+ ('order_id', '=', po.id),
+ ('product_id', '=', product.id)
+ ], limit=1)
+ stock_move = self.env['stock.move'].search([
+ ('purchase_line_id', '=', po_line.id)
+ ], limit=1)
+ picking_in = stock_move.picking_id
+ result_date = picking_in.date_done if picking_in else None
+ if result_date:
+ status = "Early" if result_date < eta else "Delay"
+ result_date_str = result_date.strftime('%m/%d/%Y')
+ eta_str = eta.strftime('%m/%d/%Y')
+ order.ready_to_ship_status_detail = f"Expected: {eta_str} | Realtime: {result_date_str} | {status}"
+ else:
+ order.ready_to_ship_status_detail = "On Track"
+ else:
+ order.ready_to_ship_status_detail = 'On Track' \ No newline at end of file
diff --git a/indoteknik_custom/models/user_pengajuan_tempo_request.py b/indoteknik_custom/models/user_pengajuan_tempo_request.py
index aae09cc4..87227764 100644
--- a/indoteknik_custom/models/user_pengajuan_tempo_request.py
+++ b/indoteknik_custom/models/user_pengajuan_tempo_request.py
@@ -682,6 +682,9 @@ class UserPengajuanTempoRequest(models.Model):
('active', 'in', [True, False])
])
+ def _message_get_suggested_recipients(self):
+ return {}
+
def format_currency(self, number):
number = int(number)
return "{:,}".format(number).replace(',', '.') \ No newline at end of file
diff --git a/indoteknik_custom/views/sale_order.xml b/indoteknik_custom/views/sale_order.xml
index a599a7b8..0fabf279 100755
--- a/indoteknik_custom/views/sale_order.xml
+++ b/indoteknik_custom/views/sale_order.xml
@@ -107,6 +107,7 @@
<t t-esc="' to '"/>
<field name="eta_date" readonly="1"/>
<field name="expected_ready_to_ship"/>
+ <field name="ready_to_ship_status_detail"/>
<field name="flash_sale"/>
<field name="margin_after_delivery_purchase"/>
<field name="percent_margin_after_delivery_purchase"/>