diff options
| -rwxr-xr-x | indoteknik_custom/models/purchase_order.py | 228 | ||||
| -rwxr-xr-x | indoteknik_custom/models/purchase_order_line.py | 2 | ||||
| -rwxr-xr-x | indoteknik_custom/views/purchase_order.xml | 19 |
3 files changed, 4 insertions, 245 deletions
diff --git a/indoteknik_custom/models/purchase_order.py b/indoteknik_custom/models/purchase_order.py index d5c08660..98b367d0 100755 --- a/indoteknik_custom/models/purchase_order.py +++ b/indoteknik_custom/models/purchase_order.py @@ -2,7 +2,6 @@ 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 @@ -90,233 +89,6 @@ 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") diff --git a/indoteknik_custom/models/purchase_order_line.py b/indoteknik_custom/models/purchase_order_line.py index 4802ebea..315795d5 100755 --- a/indoteknik_custom/models/purchase_order_line.py +++ b/indoteknik_custom/models/purchase_order_line.py @@ -51,8 +51,6 @@ class PurchaseOrderLine(models.Model): contribution_cost_service = fields.Float(string='Contribution Cost Service', compute='_compute_doc_delivery_amt') ending_price = fields.Float(string='Ending Price', compute='_compute_doc_delivery_amt') - mark_po_line = fields.Boolean(string=' ', default=False, help='centang jika anda ingin menandai PO line ini') - def _compute_doc_delivery_amt(self): for line in self: # Inisialisasi nilai default untuk field computed diff --git a/indoteknik_custom/views/purchase_order.xml b/indoteknik_custom/views/purchase_order.xml index e1ff099b..0fbbb5e7 100755 --- a/indoteknik_custom/views/purchase_order.xml +++ b/indoteknik_custom/views/purchase_order.xml @@ -37,9 +37,6 @@ <button name="button_unlock" position="after"> <button name="create_bill_pelunasan" string="Create Bill Pelunasan" type="object" class="oe_highlight" attrs="{'invisible': [('state', 'not in', ('purchase', 'done')), ('bills_pelunasan_id', '!=', False)]}"/> </button> - <button name="create_bill_dp" position="after"> - <button name="sync_price_to_so" string="Sync Price to SO" type="object" attrs="{'invisible': [('state', 'in', ['cancel'])]}"/> - </button> <field name="date_order" position="before"> <field name="sale_order_id" attrs="{'readonly': [('state', 'not in', ['draft'])]}"/> <field name="sale_order"/> @@ -142,27 +139,19 @@ </field> <field name="order_line" position="attributes"> - <attribute name="attrs">{'readonly': ['|', ('state', 'in', ['done', 'cancel']), ('has_active_invoice', '=', True)]}</attribute> + <attribute name="attrs">{'readonly': ['|', ('state', 'in', ['purchase', 'done', 'cancel']), ('has_active_invoice', '=', True)]}</attribute> </field> - - <xpath expr="//field[@name='order_line']/tree/field[1]" position="before"> - <field name="mark_po_line" widget="boolean_toggle" attrs="{'invisible': [('state', 'in', ['cancel'])]}"/> - </xpath> - - <xpath expr="//field[@name='order_line']/tree/field[@name='name']" position="attributes"> - <attribute name="attrs">{'readonly': [('parent.state', 'in', ['purchase', 'done', 'cancel'])]}</attribute> - </xpath> <xpath expr="//form/sheet/notebook/page/field[@name='order_line']/tree/field[@name='price_unit']" position="attributes"> - <attribute name="attrs">{'readonly': [('parent.state', 'in', ['purchase', 'done', 'cancel'])], 'required': True}</attribute> + <attribute name="attrs">{'readonly': [], 'required': True}</attribute> </xpath> <xpath expr="//form/sheet/notebook/page/field[@name='order_line']/tree/field[@name='taxes_id']" position="attributes"> - <attribute name="attrs">{'readonly': [('parent.state', 'in', ['purchase', 'done', 'cancel'])]}</attribute> + <attribute name="attrs">{'readonly': []}</attribute> </xpath> <xpath expr="//form/sheet/notebook/page/field[@name='order_line']/tree/field[@name='product_qty']" position="attributes"> - <attribute name="attrs">{'readonly': [('parent.state', 'in', ['purchase', 'done', 'cancel'])],'required': True}</attribute> + <attribute name="attrs">{'required': True}</attribute> </xpath> <xpath expr="//form/sheet/notebook/page[@name='purchase_delivery_invoice']" position="before"> |
