summaryrefslogtreecommitdiff
path: root/indoteknik_custom/models/purchase_order.py
diff options
context:
space:
mode:
authorIndoteknik . <it@fixcomart.co.id>2025-05-15 13:19:22 +0700
committerIndoteknik . <it@fixcomart.co.id>2025-05-15 13:19:22 +0700
commit3c170c77e4913313ca28169172dbad8c8726ad5c (patch)
tree5feb24f481271c812836b88b95870b2c0be93b45 /indoteknik_custom/models/purchase_order.py
parent2469ee37cfe854f0419a8c3fbabed5bc32bcaa6e (diff)
(andri) add button sync price to SO & penyesuaian readonly pada orderline PO
Diffstat (limited to 'indoteknik_custom/models/purchase_order.py')
-rwxr-xr-xindoteknik_custom/models/purchase_order.py228
1 files changed, 228 insertions, 0 deletions
diff --git a/indoteknik_custom/models/purchase_order.py b/indoteknik_custom/models/purchase_order.py
index 98b367d0..d5c08660 100755
--- a/indoteknik_custom/models/purchase_order.py
+++ b/indoteknik_custom/models/purchase_order.py
@@ -2,6 +2,7 @@ from odoo import fields, models, api, _
from odoo.exceptions import AccessError, UserError, ValidationError
from dateutil.relativedelta import relativedelta
from datetime import datetime, timedelta
+from odoo.tools import float_compare
import logging
from pytz import timezone, utc
import io
@@ -89,6 +90,233 @@ class PurchaseOrder(models.Model):
store_name = fields.Char(string='Nama Toko')
purchase_order_count = fields.Integer('Purchase Order Count', related='partner_id.purchase_order_count')
+
+
+ def sync_price_to_so(self):
+ updated_lines = []
+ skipped_lines = [] # Untuk melacak line yang dilewati karena harga sudah sama
+ affected_so_ids = set() # Untuk melacak SO mana saja yang terkena dampak
+
+ for order in self:
+ # Filter hanya line-line yang ditandai
+ marked_lines = order.order_line.filtered(lambda l: l.mark_po_line)
+
+ if not marked_lines:
+ return {
+ 'type': 'ir.actions.client',
+ 'tag': 'display_notification',
+ 'params': {
+ 'title': _('Info'),
+ 'message': _('Tidak ada item yang ditandai untuk disinkronkan'),
+ 'sticky': False,
+ 'type': 'info',
+ }
+ }
+
+ # Cek apakah ada referensi ke sale order
+ if not order.sale_order_id and not order.order_sales_match_line:
+ raise UserError(_("Tidak ada Sales Order yang terkait dengan Purchase Order ini!"))
+
+ # Jika PO dibuat dari SO langsung
+ if order.sale_order_id:
+ affected_so_ids.add(order.sale_order_id.id)
+ for po_line in marked_lines:
+ # Cari SO line yang terkait
+ so_line = po_line.so_line_id
+ if not so_line and po_line.product_id:
+ # Coba cari berdasarkan product jika tidak ada referensi langsung
+ so_line = self.env['sale.order.line'].search([
+ ('product_id', '=', po_line.product_id.id),
+ ('order_id', '=', order.sale_order_id.id)
+ ], limit=1)
+
+ if so_line:
+ old_price = so_line.purchase_price
+
+ # Cek apakah harga sudah sama
+ if float_compare(old_price, po_line.price_unit, precision_digits=2) == 0:
+ skipped_lines.append({
+ 'product': po_line.product_id.display_name,
+ 'so_name': so_line.order_id.name,
+ 'so_id': so_line.order_id.id,
+ 'price': old_price,
+ 'currency': order.currency_id or so_line.order_id.currency_id
+ })
+ else:
+ so_line.purchase_price = po_line.price_unit
+ updated_lines.append({
+ 'product': po_line.product_id.display_name,
+ 'so_line_id': so_line.id,
+ 'so_name': so_line.order_id.name,
+ 'so_id': so_line.order_id.id,
+ 'old_price': old_price,
+ 'new_price': po_line.price_unit,
+ 'currency': order.currency_id or so_line.order_id.currency_id
+ })
+
+ # Hapus tanda setelah sinkronisasi
+ po_line.mark_po_line = False
+
+ # Jika PO terkait dengan beberapa SO (melalui order_sales_match_line)
+ elif order.order_sales_match_line:
+ for po_line in marked_lines:
+ # Cari match lines yang sesuai dengan product di PO line
+ match_lines = order.order_sales_match_line.filtered(
+ lambda m: m.product_id.id == po_line.product_id.id
+ )
+
+ for match_line in match_lines:
+ if match_line.sale_id:
+ affected_so_ids.add(match_line.sale_id.id)
+
+ so_line = match_line.sale_line_id
+ if so_line:
+ old_price = so_line.purchase_price
+
+ # Cek apakah harga sudah sama
+ if float_compare(old_price, po_line.price_unit, precision_digits=2) == 0:
+ skipped_lines.append({
+ 'product': po_line.product_id.display_name,
+ 'so_name': so_line.order_id.name,
+ 'so_id': so_line.order_id.id,
+ 'price': old_price,
+ 'currency': order.currency_id or so_line.order_id.currency_id
+ })
+ else:
+ so_line.purchase_price = po_line.price_unit
+ updated_lines.append({
+ 'product': po_line.product_id.display_name,
+ 'so_line_id': so_line.id,
+ 'so_name': so_line.order_id.name,
+ 'so_id': so_line.order_id.id,
+ 'old_price': old_price,
+ 'new_price': po_line.price_unit,
+ 'currency': order.currency_id or so_line.order_id.currency_id
+ })
+
+ # Hapus tanda setelah sinkronisasi
+ po_line.mark_po_line = False
+
+ # Ambil data SO yang terkena dampak
+ affected_sales_orders = self.env['sale.order'].browse(list(affected_so_ids))
+
+ # Buat pesan untuk log
+ message_body = ""
+
+ # Jika ada line yang diupdate
+ if updated_lines:
+ # Kelompokkan perubahan berdasarkan SO
+ changes_by_so = {}
+ for line in updated_lines:
+ so_id = line['so_id']
+ if so_id not in changes_by_so:
+ changes_by_so[so_id] = []
+ changes_by_so[so_id].append(line)
+
+ # Buat pesan untuk line yang diupdate
+ message_body += f"<p><strong>Harga Purchase pada Sales Order telah diperbarui dari <a href='/web#id={self.id}&model=purchase.order&view_type=form'>{self.name}</a>:</strong></p>"
+
+ for so_id, lines in changes_by_so.items():
+ so = self.env['sale.order'].browse(so_id)
+ # Buat link ke SO yang bisa diklik
+ message_body += f"<p><strong>Sales Order: <a href='/web#id={so.id}&model=sale.order&view_type=form'>{so.name}</a></strong></p><ul>"
+ for line in lines:
+ # Format harga dalam format mata uang
+ currency = line['currency']
+ old_price_formatted = self.env['ir.qweb.field.monetary'].value_to_html(line['old_price'], {'display_currency': currency})
+ new_price_formatted = self.env['ir.qweb.field.monetary'].value_to_html(line['new_price'], {'display_currency': currency})
+
+ message_body += f"<li>{line['product']}:<br/>{old_price_formatted} → {new_price_formatted}</li>"
+ message_body += "</ul>"
+
+ # Jika ada line yang dilewati karena harga sudah sama
+ if skipped_lines:
+ # Kelompokkan berdasarkan SO
+ skipped_by_so = {}
+ for line in skipped_lines:
+ so_id = line['so_id']
+ if so_id not in skipped_by_so:
+ skipped_by_so[so_id] = []
+ skipped_by_so[so_id].append(line)
+
+ # Tambahkan pesan untuk line yang dilewati
+ if message_body:
+ message_body += "<p><strong>Item berikut dilewati karena harga sudah sama:</strong></p>"
+ else:
+ message_body += f"<p><strong>Tidak ada perubahan harga untuk PO <a href='/web#id={self.id}&model=purchase.order&view_type=form'>{self.name}</a>:</strong></p>"
+ message_body += "<p><strong>Item berikut sudah memiliki harga yang sama di SO:</strong></p>"
+
+ for so_id, lines in skipped_by_so.items():
+ so = self.env['sale.order'].browse(so_id)
+ message_body += f"<p><strong>Sales Order: <a href='/web#id={so.id}&model=sale.order&view_type=form'>{so.name}</a></strong></p><ul>"
+ for line in lines:
+ # Format harga dalam format mata uang
+ currency = line['currency']
+ price_formatted = self.env['ir.qweb.field.monetary'].value_to_html(line['price'], {'display_currency': currency})
+
+ message_body += f"<li>{line['product']}: {price_formatted}</li>"
+ message_body += "</ul>"
+
+ # Posting log message jika ada isi
+ if message_body:
+ subject = "Price Sync to SO" if updated_lines else "Price Sync - Harga Sama"
+ self.message_post(body=message_body, subject=subject)
+
+ # Update juga log di setiap SO yang terkena dampak
+ if updated_lines:
+ for so in affected_sales_orders:
+ so_lines = [line for line in updated_lines if line['so_id'] == so.id]
+ if so_lines:
+ # Buat link ke PO yang bisa diklik
+ so_message = f"<p><strong>Harga Purchase diperbarui dari <a href='/web#id={self.id}&model=purchase.order&view_type=form'>{self.name}</a>:</strong></p><ul>"
+ for line in so_lines:
+ # Format harga dalam format mata uang
+ currency = line['currency']
+ old_price_formatted = self.env['ir.qweb.field.monetary'].value_to_html(line['old_price'], {'display_currency': currency})
+ new_price_formatted = self.env['ir.qweb.field.monetary'].value_to_html(line['new_price'], {'display_currency': currency})
+
+ so_message += f"<li>{line['product']}:<br/>{old_price_formatted} → {new_price_formatted}</li>"
+ so_message += "</ul>"
+ so.message_post(body=so_message, subject=f"Price Updated from PO {self.name}")
+
+ # Recalculate margins
+ if updated_lines and hasattr(self, 'compute_total_margin'):
+ self.compute_total_margin()
+
+ # Recalculate margins di SO juga
+ if updated_lines:
+ for so in affected_sales_orders:
+ if hasattr(so, 'compute_total_margin'):
+ so.compute_total_margin()
+
+ # Tentukan pesan notifikasi dan tipe
+ if updated_lines and skipped_lines:
+ message = f"{len(updated_lines)} item diperbarui dan {len(skipped_lines)} item dilewati karena harga sudah sama"
+ title = "Sukses!"
+ notification_type = "success"
+ elif updated_lines:
+ message = f"{len(updated_lines)} item telah diperbarui harganya di {len(affected_so_ids)} Sales Order"
+ title = "Sukses!"
+ notification_type = "success"
+ elif skipped_lines:
+ message = f"item tersebut ({len(skipped_lines)}) sudah memiliki harga yang sama"
+ title = "Info"
+ notification_type = "info"
+ else:
+ message = "Tidak ada line yang berhasil diperbarui"
+ title = "Info"
+ notification_type = "info"
+
+ return {
+ 'type': 'ir.actions.client',
+ 'tag': 'display_notification',
+ 'params': {
+ 'title': _(title),
+ 'message': _(message),
+ 'sticky': False,
+ 'type': notification_type,
+ }
+ }
# cek payment term
def _check_payment_term(self):
_logger.info("Check Payment Term Terpanggil")