diff options
| author | Azka Nathan <darizkyfaz@gmail.com> | 2025-07-14 17:03:30 +0700 |
|---|---|---|
| committer | Azka Nathan <darizkyfaz@gmail.com> | 2025-07-14 17:03:30 +0700 |
| commit | 2eb64e50e1d41af96cf29097a5781d4df08d5f16 (patch) | |
| tree | 4f7e45008821a62959585533ca20527627fdd8dc | |
| parent | ee77ca5ec2947ba672c7becee3ecb583f4f25916 (diff) | |
| parent | 1b70be54f939974c0232abf215f8a432b6c15244 (diff) | |
Merge branch 'odoo-backup' of bitbucket.org:altafixco/indoteknik-addons into odoo-backup
| -rw-r--r-- | indoteknik_custom/models/account_move.py | 18 | ||||
| -rw-r--r-- | indoteknik_custom/models/commision.py | 5 | ||||
| -rwxr-xr-x | indoteknik_custom/models/sale_order.py | 4 | ||||
| -rw-r--r-- | indoteknik_custom/models/stock_picking.py | 102 | ||||
| -rw-r--r-- | indoteknik_custom/views/customer_commision.xml | 1 | ||||
| -rw-r--r-- | indoteknik_custom/views/ir_sequence.xml | 2 | ||||
| -rw-r--r-- | indoteknik_custom/views/mail_template_invoice_reminder.xml | 8 |
7 files changed, 87 insertions, 53 deletions
diff --git a/indoteknik_custom/models/account_move.py b/indoteknik_custom/models/account_move.py index 5ac1c6e5..72ac5452 100644 --- a/indoteknik_custom/models/account_move.py +++ b/indoteknik_custom/models/account_move.py @@ -85,7 +85,7 @@ class AccountMove(models.Model): today + timedelta(days=7), ] - partner = self.env['res.partner'].search([('name', 'ilike', 'FLYNINDO MEGA PERSADA')], limit=1) + partner = self.env['res.partner'].search([('name', 'ilike', 'BANGUNAN TEKNIK GRUP')], limit=1) if not partner: _logger.info("Partner tidak ditemukan.") return @@ -149,11 +149,27 @@ class AccountMove(models.Model): 'email_to': 'andrifebriyadiputra@gmail.com', # Ubah ke partner.email untuk produksi 'email_from': 'finance@indoteknik.co.id', 'body_html': body_html, + 'reply_to': f'invoice+account.move_{invs[0].id}@indoteknik.co.id', } _logger.info(f"VALUES: {values}") template.send_mail(invs[0].id, force_send=True, email_values=values) + + # Default System User + user_system = self.env['res.users'].browse(25) + system_id = user_system.partner_id.id if user_system else False + _logger.info(f"System User: {user_system.name} ({user_system.id})") + _logger.info(f"System User ID: {system_id}") + + for inv in invs: + inv.message_post( + subject=subject, + body=body_html, + subtype_id=self.env.ref('mail.mt_note').id, + author_id=system_id, + ) + _logger.info(f"Reminder terkirim ke {partner.name} ({values['email_to']}) → {len(invs)} invoice") diff --git a/indoteknik_custom/models/commision.py b/indoteknik_custom/models/commision.py index 97184cdb..26b5df37 100644 --- a/indoteknik_custom/models/commision.py +++ b/indoteknik_custom/models/commision.py @@ -193,6 +193,7 @@ class CustomerCommision(models.Model): commision_amt_text = fields.Char(string='Amount Text', compute='compute_delivery_amt_text') total_cashback_text = fields.Char(string='Cashback Text', compute='compute_total_cashback_text') total_dpp = fields.Float(string='Total DPP', compute='_compute_total_dpp') + biaya_lain_lain = fields.Float(string='Biaya Lain-lain') commision_type = fields.Selection([ ('fee', 'Fee'), ('cashback', 'Cashback'), @@ -363,13 +364,13 @@ class CustomerCommision(models.Model): else: self.cashback = 0 self.total_commision = 0 - + def _compute_total_dpp(self): for data in self: total_dpp = 0 for line in data.commision_lines: total_dpp = total_dpp + line.dpp - data.total_dpp = total_dpp + data.total_dpp = total_dpp - data.biaya_lain_lain @api.model def create(self, vals): diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index 591951ca..e197a6af 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -3048,6 +3048,10 @@ class SaleOrder(models.Model): if picking.state == 'assigned': picking.carrier_id = vals['carrier_id'] + for picking in order.picking_ids: + if picking.state not in ['done', 'cancel', 'assigned']: + picking.write({'carrier_id': vals['carrier_id']}) + try: helper_ids = self._get_helper_ids() if str(self.env.user.id) in helper_ids: diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 6f260dfa..b9c90551 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -20,8 +20,10 @@ _logger = logging.getLogger(__name__) _biteship_url = "https://api.biteship.com/v1" biteship_api_key = "biteship_live.eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiaW5kb3Rla25payIsInVzZXJJZCI6IjY3MTViYTJkYzVkMjdkMDAxMjRjODk2MiIsImlhdCI6MTc0MTE1NTU4M30.pbFCai9QJv8iWhgdosf8ScVmEeP3e5blrn33CHe7Hgo" + + # biteship_api_key = "biteship_test.eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiSW5kb3Rla25payIsInVzZXJJZCI6IjY3MTViYTJkYzVkMjdkMDAxMjRjODk2MiIsImlhdCI6MTcyOTQ5ODAwMX0.L6C73couP4-cgVEfhKI2g7eMCMo3YOFSRZhS-KSuHNA" - + class StockPicking(models.Model): _inherit = 'stock.picking' @@ -119,7 +121,7 @@ class StockPicking(models.Model): waybill_id = fields.One2many(comodel_name='airway.bill', inverse_name='do_id', string='Airway Bill') purchase_representative_id = fields.Many2one('res.users', related='move_lines.purchase_line_id.order_id.user_id', string="Purchase Representative") - carrier_id = fields.Many2one('delivery.carrier', string='Shipping Method') + carrier_id = fields.Many2one('delivery.carrier', string='Shipping Method', tracking=3) shipping_status = fields.Char(string='Shipping Status', compute="_compute_shipping_status") date_reserved = fields.Datetime(string="Date Reserved", help='Tanggal ter-reserved semua barang nya', copy=False) status_printed = fields.Selection([ @@ -278,16 +280,22 @@ class StockPicking(models.Model): biteship_driver_plate_number = fields.Char('Biteship Driver Plate Number') biteship_courier_link = fields.Char('Biteship Courier Link') biteship_shipping_status = fields.Char('Biteship Shipping Status', help="Status pengiriman dari Biteship") - biteship_shipping_price = fields.Monetary('Biteship Shipping Price', currency_field='currency_id', help="Harga pengiriman dari Biteship") + biteship_shipping_price = fields.Monetary('Biteship Shipping Price', currency_field='currency_id', + help="Harga pengiriman dari Biteship") currency_id = fields.Many2one('res.currency', related='sale_id.currency_id', string='Currency', readonly=True) final_seq = fields.Float(string='Remaining Time') - shipping_method_so_id = fields.Many2one('delivery.carrier', string='Shipping Method', related='sale_id.carrier_id', help="Shipping Method yang digunakan di SO") - shipping_option_so_id = fields.Many2one('shipping.option', string='Shipping Option', related='sale_id.shipping_option_id' , help="Shipping Option yang digunakan di SO") + shipping_method_so_id = fields.Many2one('delivery.carrier', string='Shipping Method', related='sale_id.carrier_id', + help="Shipping Method yang digunakan di SO", tracking=3) + shipping_option_so_id = fields.Many2one('shipping.option', string='Shipping Option', + related='sale_id.shipping_option_id', + help="Shipping Option yang digunakan di SO", tracking=3) select_shipping_option_so = fields.Selection([ ('biteship', 'Biteship'), ('custom', 'Custom'), - ], string='Shipping Type', related='sale_id.select_shipping_option', help="Shipping Type yang digunakan di SO") - state_packing = fields.Selection([('not_packing', 'Belum Packing'), ('packing_done', 'Sudah Packing')], string='Packing Status') + ], string='Shipping Type', related='sale_id.select_shipping_option', help="Shipping Type yang digunakan di SO", + tracking=3) + state_packing = fields.Selection([('not_packing', 'Belum Packing'), ('packing_done', 'Sudah Packing')], + string='Packing Status') approval_invoice_date_id = fields.Many2one('approval.invoice.date', string='Approval Invoice Date') last_update_date_doc_kirim = fields.Datetime(string='Last Update Tanggal Kirim', copy=False) update_date_doc_kirim_add = fields.Boolean(string='Update Tanggal Kirim Lewat ADD') @@ -298,7 +306,7 @@ class StockPicking(models.Model): if not self.name or not self.origin: return False return f"{self.name}" - + def _download_pod_photo(self, url): """Mengunduh foto POD dari URL""" try: @@ -307,7 +315,7 @@ class StockPicking(models.Model): return base64.b64encode(response.content) except Exception as e: raise UserError(f"Gagal mengunduh foto POD: {str(e)}") - + def _parse_datetime(self, dt_str): """Parse datetime string dari format KGX""" try: @@ -318,37 +326,37 @@ class StockPicking(models.Model): return datetime.strptime(dt_str, '%Y-%m-%dT%H:%M:%S') except ValueError: return False - + def action_get_kgx_pod(self, shipment=False): self.ensure_one() - + awb_number = shipment or self._get_kgx_awb_number() if not awb_number: raise UserError("Nomor AWB tidak dapat dibuat, pastikan picking memiliki name dan origin") - + url = "https://kgx.co.id/get_detail_awb" headers = {'Content-Type': 'application/json'} - payload = {"params" : {'awb_number': awb_number}} - + payload = {"params": {'awb_number': awb_number}} + try: response = requests.post(url, headers=headers, data=json.dumps(payload)) response.raise_for_status() data = response.json() - + if data.get('result', {}).get('data', []): pod_data = data['result']['data'][0].get('connote_pod', {}) photo_url = pod_data.get('photo') - + self.kgx_pod_photo_url = photo_url self.kgx_pod_signature = pod_data.get('signature') self.kgx_pod_receiver = pod_data.get('receiver') self.kgx_pod_receive_time = self._parse_datetime(pod_data.get('timeReceive')) self.driver_arrival_date = self._parse_datetime(pod_data.get('timeReceive')) - + return data else: raise UserError(f"Tidak ditemukan data untuk AWB: {awb_number}") - + except requests.exceptions.RequestException as e: raise UserError(f"Gagal mengambil data POD: {str(e)}") @@ -695,8 +703,9 @@ class StockPicking(models.Model): raise UserError(f"Order ini sudah dikirim ke Biteship. Dengan Tracking Id: {self.biteship_tracking_id}") if self.sale_id.select_shipping_option == 'custom': - raise UserError("Shipping Option pada Sales Order ini adalah *Custom*. Tidak dapat dikirim melalui Biteship.") - + raise UserError( + "Shipping Option pada Sales Order ini adalah *Custom*. Tidak dapat dikirim melalui Biteship.") + def is_courier_need_coordinates(service_code): return service_code in [ "instant", "same_day", "instant_car", @@ -793,11 +802,11 @@ class StockPicking(models.Model): self.message_post( body=f"Biteship berhasil dilakukan.<br/>" - f"Kurir: {self.carrier_id.name}<br/>" - f"Tracking ID: {self.biteship_tracking_id or '-'}<br/>" - f"Resi: {waybill_id or '-'}<br/>" - f"Reference: {self.name}<br/>" - f"SO: {self.sale_id.name}", + f"Kurir: {self.carrier_id.name}<br/>" + f"Tracking ID: {self.biteship_tracking_id or '-'}<br/>" + f"Resi: {waybill_id or '-'}<br/>" + f"Reference: {self.name}<br/>" + f"SO: {self.sale_id.name}", message_type="comment" ) @@ -939,6 +948,9 @@ class StockPicking(models.Model): pending_section = None # Invoice values. invoice_vals = order._prepare_invoice() + invoice_date = self.date_done + invoice_vals['date'] = invoice_date + invoice_vals['invoice_date'] = invoice_date # Invoice line values (keep only necessary sections). for line in self.move_ids_without_package: po_line = self.env['purchase.order.line'].search( @@ -1304,7 +1316,6 @@ class StockPicking(models.Model): ) ) - self.validation_minus_onhand_quantity() self.responsible = self.env.user.id # self.send_koli_to_so() @@ -1355,7 +1366,7 @@ class StockPicking(models.Model): if 'BU/PUT' in self.name: self.automatic_reserve_product() return res - + def automatic_reserve_product(self): if self.state == 'done': po = self.env['purchase.order'].search([ @@ -1373,11 +1384,12 @@ class StockPicking(models.Model): continue invoice = self.env['account.move'].search( - [('sale_id', '=', picking.sale_id.id), ('state', 'not in', ['draft', 'cancel']), ('move_type', '=', 'out_invoice')], limit=1) + [('sale_id', '=', picking.sale_id.id), ('state', 'not in', ['draft', 'cancel']), + ('move_type', '=', 'out_invoice')], limit=1) if not invoice: continue - + if not picking.so_lama and invoice and (not picking.date_doc_kirim or not invoice.invoice_date): raise UserError("Tanggal Kirim atau Tanggal Invoice belum diisi!") @@ -1661,7 +1673,7 @@ class StockPicking(models.Model): self.ensure_one() order = self.env['sale.order'].search([('name', '=', self.sale_id.name)], limit=1) - + sale_order_delay = self.env['sale.order.delay'].search([('so_number', '=', order.name)], limit=1) product_shipped = [] @@ -1676,11 +1688,12 @@ class StockPicking(models.Model): 'delivery_order': { 'name': self.name, 'carrier': self.carrier_id.name or '-', - 'service' : order.delivery_service_type or '-', + 'service': order.delivery_service_type or '-', 'receiver_name': '', 'receiver_city': '' }, - 'delivered_date': self.driver_departure_date.strftime('%d %b %Y') if self.driver_departure_date != False else '-', + 'delivered_date': self.driver_departure_date.strftime( + '%d %b %Y') if self.driver_departure_date != False else '-', 'delivered': False, 'status': self.shipping_status, 'waybill_number': self.delivery_tracking_no or '-', @@ -1703,7 +1716,7 @@ class StockPicking(models.Model): elif sale_order_delay.status == 'early': day_start = day_start - sale_order_delay.days_delayed day_end = day_end - sale_order_delay.days_delayed - + eta_start = order.date_order + timedelta(days=day_start) eta_end = order.date_order + timedelta(days=day_end) formatted_eta = f"{eta_start.strftime('%d %b')} - {eta_end.strftime('%d %b %Y')}" @@ -1732,7 +1745,7 @@ class StockPicking(models.Model): "Authorization": f"Bearer {api_key}", "Content-Type": "application/json" } - + manifests = [] try: @@ -1769,9 +1782,9 @@ class StockPicking(models.Model): "manifests": [], "delivered": False } - except Exception as e : + except Exception as e: _logger.error(f"Error fetching Biteship order for picking {self.id}: {str(e)}") - return { 'error': str(e) } + return {'error': str(e)} # ACTION GET TRACKING MANUAL BITESHIP # def action_sync_biteship_tracking(self): @@ -1855,7 +1868,6 @@ class StockPicking(models.Model): return description_map.get(status, f"Status '{status}' diterima dari Biteship") - def log_biteship_event_from_webhook(self, status, timestamp, description, extra_data=None): self.ensure_one() updated_fields = {} @@ -1916,7 +1928,7 @@ class StockPicking(models.Model): # Hindari log duplikat if not self._has_existing_log(log_line): - biteship_user = self.env['res.users'].sudo().browse(15710) # ID live + biteship_user = self.env['res.users'].sudo().browse(15710) # ID live # biteship_user = self.env['res.users'].sudo().browse(15710) # ID user (cek di db) self.sudo().message_post( body=log_line, @@ -1928,7 +1940,6 @@ class StockPicking(models.Model): self.write(updated_fields) _logger.info(f"[Webhook] Updated fields on picking {self.name}: {updated_fields}") - def _has_existing_log(self, log_line): self.ensure_one() self.env.cr.execute(""" @@ -1995,8 +2006,7 @@ class StockPicking(models.Model): days_end = self.sale_id.estimated_arrival_days or (self.sale_id.estimated_arrival_days + 3) start_date = self.sale_id.create_date + datetime.timedelta(days=days_start) end_date = self.sale_id.create_date + datetime.timedelta(days=days_end) - - + add_day_start = 0 add_day_end = 0 sale_order_delay = self.env['sale.order.delay'].search([('so_number', '=', self.sale_id.name)], limit=1) @@ -2005,11 +2015,11 @@ class StockPicking(models.Model): add_day_start = sale_order_delay.days_delayed add_day_end = sale_order_delay.days_delayed elif sale_order_delay.status == 'early': - add_day_start = -abs(sale_order_delay.days_delayed) - add_day_end = -abs(sale_order_delay.days_delayed) - - fastest_eta = start_date +datetime.timedelta(days=add_day_start + add_day_start) - + add_day_start = -abs(sale_order_delay.days_delayed) + add_day_end = -abs(sale_order_delay.days_delayed) + + fastest_eta = start_date + datetime.timedelta(days=add_day_start + add_day_start) + longest_eta = end_date + datetime.timedelta(days=add_day_end) format_time = '%d %b %Y' diff --git a/indoteknik_custom/views/customer_commision.xml b/indoteknik_custom/views/customer_commision.xml index 1c17bf63..514e6284 100644 --- a/indoteknik_custom/views/customer_commision.xml +++ b/indoteknik_custom/views/customer_commision.xml @@ -103,6 +103,7 @@ <field name="notification" readonly="1"/> <!-- <field name="status" readonly="1"/>--> <field name="payment_status" readonly="1"/> + <field name="biaya_lain_lain"/> <field name="total_dpp"/> </group> </group> diff --git a/indoteknik_custom/views/ir_sequence.xml b/indoteknik_custom/views/ir_sequence.xml index d9b93ff3..f2b42c3b 100644 --- a/indoteknik_custom/views/ir_sequence.xml +++ b/indoteknik_custom/views/ir_sequence.xml @@ -164,7 +164,7 @@ <record id="sequence_commision_fee" model="ir.sequence"> <field name="name">Customer Commision Fee</field> <field name="code">customer.commision.fee</field> - <field name="prefix">FE/%(year)s/</field> + <field name="prefix">CC/%(year)s/</field> <field name="padding">5</field> <field name="number_next">1</field> <field name="number_increment">1</field> diff --git a/indoteknik_custom/views/mail_template_invoice_reminder.xml b/indoteknik_custom/views/mail_template_invoice_reminder.xml index 8b3b9880..21055eb0 100644 --- a/indoteknik_custom/views/mail_template_invoice_reminder.xml +++ b/indoteknik_custom/views/mail_template_invoice_reminder.xml @@ -13,7 +13,7 @@ <p>Berikut adalah daftar invoice Anda yang mendekati atau telah jatuh tempo:</p> - <table border="1" cellpadding="6" cellspacing="0" style="border-collapse: collapse; width: 100%;"> + <table border="1" cellpadding="4" cellspacing="0" style="border-collapse: collapse; width: 100%; font-size: 12px"> <thead> <tr style="background-color: #f2f2f2;" align="left"> <th>Invoice Number</th> @@ -37,9 +37,11 @@ <br/> Widya R.<br/> Dept. Finance<br/> - PT. INDOTEKNIK DOTCOM GEMILANG</b><br/> + PT. INDOTEKNIK DOTCOM GEMILANG<br/> <img src="https://erp.indoteknik.com/api/image/ir.attachment/datas/2135765" alt="Indoteknik" style="max-width: 18%; height: auto;"></img><br/> - +62-857-1697-0374 | finance@indoteknik.co.id</p> + <a href="https://wa.me/6285716970374" target="_blank">+62-857-1697-0374</a> | + <a href="mailto:finance@indoteknik.co.id">finance@indoteknik.co.id</a> + </b></p> </div> </field> |
