From 6c91d8d4973eb4588bb2f7b7cbb8cd4b6c59baac Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Fri, 23 May 2025 11:03:57 +0700 Subject: fix expected ready to ship --- indoteknik_api/controllers/api_v1/sale_order.py | 24 +++++++++++++++++ indoteknik_custom/models/sale_order.py | 34 +++++++++++++++++++++++++ indoteknik_custom/views/sale_order.xml | 1 + 3 files changed, 59 insertions(+) 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/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/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 @@ + -- cgit v1.2.3 From abc587069b2526756b4848b53f1d20a8b5825134 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Fri, 23 May 2025 12:29:01 +0700 Subject: overwrite function --- indoteknik_custom/models/user_pengajuan_tempo_request.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/indoteknik_custom/models/user_pengajuan_tempo_request.py b/indoteknik_custom/models/user_pengajuan_tempo_request.py index 565b0315..8c2d0d94 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 -- cgit v1.2.3 From 0ba6b5b041d63adc6d5f3bc69c001d450c75742e Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Mon, 26 May 2025 08:01:18 +0700 Subject: (andri) set default urutan tabel invoices pada dunning run --- indoteknik_custom/models/dunning_run.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) 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') -- cgit v1.2.3 From bab061bc003f132e738d7ad2f9d99df903392d1a Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Mon, 26 May 2025 08:12:58 +0700 Subject: (andri) revisi log note product variant --- indoteknik_custom/models/product_template.py | 160 +++++++++++---------------- 1 file changed, 66 insertions(+), 94 deletions(-) diff --git a/indoteknik_custom/models/product_template.py b/indoteknik_custom/models/product_template.py index 5cb3da88..5480204f 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"Vendor Pricelist: 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"
  • {field_label}: '{old_val_str}' → '{new_val_str}'
  • ") if changes: + # PERBAIKAN: Hanya post ke template, HAPUS bagian log ke variants record.message_post(body=f"Updated:
      {''.join(changes)}
    ") - # log changes to product variants - # variant_message = f"Updated:
      {''.join(changes)}
    " - # 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) @@ -1222,101 +1218,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"
  • {field_label}: image added
  • ") - elif old_val_str != 'None' and new_val_str == 'None': - changes.append(f"
  • {field_label}: image removed
  • ") - else: - changes.append(f"
  • {field_label}: image updated
  • ") - else: - changes.append(f"
  • {field_label}: '{old_val_str}' → '{new_val_str}'
  • ") + old_val_str = stringify(old_value, field, record) + new_val_str = stringify(new_value, field, record) - if changes: - # Post message to variant - variant_message = "Updated:
      %s
    " % "".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"
  • {field_label}: image added
  • ") + elif old_val_str != 'None' and new_val_str == 'None': + changes.append(f"
  • {field_label}: image removed
  • ") + else: + changes.append(f"
  • {field_label}: image updated
  • ") + else: + changes.append(f"
  • {field_label}: '{old_val_str}' → '{new_val_str}'
  • ") - # 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 = "Variant Updates:
    " - - for variant_id, variant_data in variants_data.items(): - # Create clickable link using your format - variant_link = f"{variant_data['name']}
    " - template_message += f"{variant_link}
      {''.join(variant_data['changes'])}

    " - - template.message_post(body=template_message) + if changes: + # PERBAIKAN: Hanya post message ke variant, HAPUS bagian template_changes + variant_message = "Updated:
      %s
    " % "".join(changes) + record.message_post(body=variant_message) # simpan data lama dan log perubahan field def write(self, vals): -- cgit v1.2.3 From 388ad29c05f5bb8bdc4a913ea36b1975760623a0 Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Mon, 26 May 2025 08:53:51 +0700 Subject: tampil sla ready to ship di website --- indoteknik_api/controllers/api_v1/sale_order.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indoteknik_api/controllers/api_v1/sale_order.py b/indoteknik_api/controllers/api_v1/sale_order.py index e8c2c75a..4297b9fc 100644 --- a/indoteknik_api/controllers/api_v1/sale_order.py +++ b/indoteknik_api/controllers/api_v1/sale_order.py @@ -166,7 +166,7 @@ class SaleOrder(controller.Controller): 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}" - +f return self.response(data) @http.route(PREFIX_PARTNER + 'sale_order//checkout', auth='public', method=['POST', 'OPTIONS'], csrf=False) -- cgit v1.2.3 From 8d7725f4d783c43d358017f5e2bf261c10bff6c7 Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Mon, 26 May 2025 09:00:43 +0700 Subject: fix --- indoteknik_api/controllers/api_v1/sale_order.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indoteknik_api/controllers/api_v1/sale_order.py b/indoteknik_api/controllers/api_v1/sale_order.py index 4297b9fc..e8c2c75a 100644 --- a/indoteknik_api/controllers/api_v1/sale_order.py +++ b/indoteknik_api/controllers/api_v1/sale_order.py @@ -166,7 +166,7 @@ class SaleOrder(controller.Controller): 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}" -f + return self.response(data) @http.route(PREFIX_PARTNER + 'sale_order//checkout', auth='public', method=['POST', 'OPTIONS'], csrf=False) -- cgit v1.2.3