diff options
| author | Azka Nathan <darizkyfaz@gmail.com> | 2026-01-06 13:01:13 +0700 |
|---|---|---|
| committer | Azka Nathan <darizkyfaz@gmail.com> | 2026-01-06 13:01:13 +0700 |
| commit | 3c885bec3b51b35c77c983444a949783cb53e198 (patch) | |
| tree | 43f482e653a223d7625c32026d949ac7819511f5 | |
| parent | 0d60d1c77f39859754e6f1cb46efe19d3ec1df38 (diff) | |
queuing job
| -rwxr-xr-x | fixco_custom/__manifest__.py | 1 | ||||
| -rwxr-xr-x | fixco_custom/models/__init__.py | 1 | ||||
| -rwxr-xr-x | fixco_custom/models/detail_order.py | 188 | ||||
| -rw-r--r-- | fixco_custom/models/purchase_order.py | 9 | ||||
| -rw-r--r-- | fixco_custom/models/queue_job.py | 46 | ||||
| -rwxr-xr-x | fixco_custom/models/sale.py | 12 | ||||
| -rw-r--r-- | fixco_custom/models/sale_order_multi_invoices.py | 81 | ||||
| -rwxr-xr-x | fixco_custom/models/stock_picking.py | 1 | ||||
| -rw-r--r-- | fixco_custom/models/upload_cancel_picking.py | 12 | ||||
| -rw-r--r-- | fixco_custom/models/upload_ginee.py | 22 | ||||
| -rwxr-xr-x | fixco_custom/security/ir.model.access.csv | 1 | ||||
| -rw-r--r-- | fixco_custom/views/queue_job.xml | 54 | ||||
| -rw-r--r-- | fixco_custom/views/sale_order_multi_invoices.xml | 2 | ||||
| -rwxr-xr-x | fixco_custom/views/stock_picking.xml | 2 |
14 files changed, 292 insertions, 140 deletions
diff --git a/fixco_custom/__manifest__.py b/fixco_custom/__manifest__.py index aef74df..0543ec2 100755 --- a/fixco_custom/__manifest__.py +++ b/fixco_custom/__manifest__.py @@ -49,6 +49,7 @@ 'views/token_log.xml', 'views/wizard_purchase_pricelist.xml', 'views/upload_cancel_picking.xml', + 'views/queue_job.xml', ], 'demo': [], 'css': [], diff --git a/fixco_custom/models/__init__.py b/fixco_custom/models/__init__.py index 47a4176..c13c9a3 100755 --- a/fixco_custom/models/__init__.py +++ b/fixco_custom/models/__init__.py @@ -37,3 +37,4 @@ from . import stock_picking_return from . import account_move_reversal from . import upload_cancel_picking from . import product_supplierinfo +from . import queue_job diff --git a/fixco_custom/models/detail_order.py b/fixco_custom/models/detail_order.py index 41a5466..b8e6cc3 100755 --- a/fixco_custom/models/detail_order.py +++ b/fixco_custom/models/detail_order.py @@ -5,6 +5,7 @@ import requests import json import hmac import base64 +from datetime import datetime from hashlib import sha256 import logging @@ -283,96 +284,105 @@ class DetailOrder(models.Model): # First check if a sale order with this reference already exists existing_order = self.env['sale.order'].search([('order_reference', '=', order_id)], limit=1) - if order_status == 'CANCELLED': - external_order_id = json_data.get('data', [{}])[0].get('externalOrderId') - order_id = json_data.get('data', [{}])[0].get('orderId') - - # Try to find existing SO - existing_order = self.env['sale.order'].search([ - '|', - ('invoice_mp', '=', external_order_id), - ('client_order_ref', '=', order_id) - ], limit=1) - - if existing_order: - if existing_order.state == 'sale': - # Cancel all pickings linked to this order - for picking in existing_order.picking_ids: - if picking.state not in ['cancel', 'done']: - picking.action_cancel() + create_at_str = json_data.get('data', [{}])[0].get('createAt') + + if create_at_str: + create_at = datetime.strptime(create_at_str, "%Y-%m-%dT%H:%M:%SZ") + + cutoff = datetime(2026, 1, 1) # 1 Jan 2026 UTC + + if create_at >= cutoff: + + if order_status == 'CANCELLED': + external_order_id = json_data.get('data', [{}])[0].get('externalOrderId') + order_id = json_data.get('data', [{}])[0].get('orderId') + + # Try to find existing SO + existing_order = self.env['sale.order'].search([ + '|', + ('invoice_mp', '=', external_order_id), + ('client_order_ref', '=', order_id) + ], limit=1) + + if existing_order: + if existing_order.state == 'sale': + # Cancel all pickings linked to this order + for picking in existing_order.picking_ids: + if picking.state not in ['cancel', 'done']: + picking.action_cancel() + self.sale_id = existing_order.id + self.execute_status = 'cancelled_so_picking' + existing_order.action_cancel() + else: + existing_order.action_cancel() + self.sale_id = existing_order.id + self.execute_status = 'cancelled_so' + + else: + # If no existing SO, create one, then cancel + data = self.prepare_data_so(json_data) + order_lines, product_not_found = self.prepare_data_so_line(json_data) + data['order_line'] = order_lines + sale_order = self.env['sale.order'].create(data) + + self.sale_id = sale_order.id + sale_order.order_reference = order_id + sale_order.address = json_data.get('data', [{}])[0].get('shippingAddressInfo', []).get('fullAddress', []) + sale_order.note_by_buyer = json_data.get('data', [{}])[0].get('extraInfo', []).get('noteByBuyer', []) + + sale_order.action_cancel() + self.execute_status = 'cancelled_so_created' + + return + + if existing_order: + # If order already exists, just update the references self.sale_id = existing_order.id - self.execute_status = 'cancelled_so_picking' - existing_order.action_cancel() - else: - existing_order.action_cancel() - self.sale_id = existing_order.id - self.execute_status = 'cancelled_so' - - else: - # If no existing SO, create one, then cancel - data = self.prepare_data_so(json_data) - order_lines, product_not_found = self.prepare_data_so_line(json_data) - data['order_line'] = order_lines - sale_order = self.env['sale.order'].create(data) - - self.sale_id = sale_order.id - sale_order.order_reference = order_id - sale_order.address = json_data.get('data', [{}])[0].get('shippingAddressInfo', []).get('fullAddress', []) - sale_order.note_by_buyer = json_data.get('data', [{}])[0].get('extraInfo', []).get('noteByBuyer', []) - - sale_order.action_cancel() - self.execute_status = 'cancelled_so_created' - - return - - if existing_order: - # If order already exists, just update the references - self.sale_id = existing_order.id - self.execute_status = 'already_so' - return # Exit early since we don't need to create anything - - if order_status != 'PENDING_PAYMENT': - if order_status in ('PARTIALLY_PAID', 'PAID'): - data['order_line'] = order_lines - sale_order = self.env['sale.order'].create(data) - - self.sale_id = sale_order.id - sale_order.order_reference = order_id - sale_order.address = json_data.get('data', [{}])[0].get('shippingAddressInfo', []).get('fullAddress', []) - sale_order.note_by_buyer = json_data.get('data', [{}])[0].get('extraInfo', []).get('noteByBuyer', []) - if not product_not_found: - sale_order.action_confirm() - # self.picking_id = sale_order.picking_ids[0].id - # self.picking_id.order_reference = order_id - # self.picking_id.invoice_mp = sale_order.invoice_mp - # self.picking_id.carrier = sale_order.carrier - # self.picking_id.address = json_data.get('data', [{}])[0].get('shippingAddressInfo', []).get('fullAddress', []) - # self.picking_id.note_by_buyer = json_data.get('data', [{}])[0].get('extraInfo', []).get('noteByBuyer', []) - - self.execute_status = 'so_confirm' - else: - self.execute_status = 'so_draft' - else: - # For other statuses, create new order only if it doesn't exist - data['order_line'] = order_lines - sale_order = self.env['sale.order'].create(data) - - self.sale_id = sale_order.id - sale_order.order_reference = order_id - sale_order.address = json_data.get('data', [{}])[0].get('shippingAddressInfo', []).get('fullAddress', []) - sale_order.note_by_buyer = json_data.get('data', [{}])[0].get('extraInfo', []).get('noteByBuyer', []) - if not product_not_found: - sale_order.action_confirm() - # self.picking_id = sale_order.picking_ids[0].id - # self.picking_id.order_reference = order_id - # self.picking_id.invoice_mp = sale_order.invoice_mp - # self.picking_id.carrier = sale_order.carrier - # self.picking_id.address = json_data.get('data', [{}])[0].get('shippingAddressInfo', []).get('fullAddress', []) - # self.picking_id.note_by_buyer = json_data.get('data', [{}])[0].get('extraInfo', []).get('noteByBuyer', []) - - self.execute_status = 'so_confirm' - else: - self.execute_status = 'so_draft' + self.execute_status = 'already_so' + return # Exit early since we don't need to create anything + + if order_status != 'PENDING_PAYMENT': + if order_status in ('PARTIALLY_PAID', 'PAID'): + data['order_line'] = order_lines + sale_order = self.env['sale.order'].create(data) + + self.sale_id = sale_order.id + sale_order.order_reference = order_id + sale_order.address = json_data.get('data', [{}])[0].get('shippingAddressInfo', []).get('fullAddress', []) + sale_order.note_by_buyer = json_data.get('data', [{}])[0].get('extraInfo', []).get('noteByBuyer', []) + if not product_not_found: + sale_order.action_confirm() + # self.picking_id = sale_order.picking_ids[0].id + # self.picking_id.order_reference = order_id + # self.picking_id.invoice_mp = sale_order.invoice_mp + # self.picking_id.carrier = sale_order.carrier + # self.picking_id.address = json_data.get('data', [{}])[0].get('shippingAddressInfo', []).get('fullAddress', []) + # self.picking_id.note_by_buyer = json_data.get('data', [{}])[0].get('extraInfo', []).get('noteByBuyer', []) + + self.execute_status = 'so_confirm' + else: + self.execute_status = 'so_draft' + else: + # For other statuses, create new order only if it doesn't exist + data['order_line'] = order_lines + sale_order = self.env['sale.order'].create(data) + + self.sale_id = sale_order.id + sale_order.order_reference = order_id + sale_order.address = json_data.get('data', [{}])[0].get('shippingAddressInfo', []).get('fullAddress', []) + sale_order.note_by_buyer = json_data.get('data', [{}])[0].get('extraInfo', []).get('noteByBuyer', []) + if not product_not_found: + sale_order.action_confirm() + # self.picking_id = sale_order.picking_ids[0].id + # self.picking_id.order_reference = order_id + # self.picking_id.invoice_mp = sale_order.invoice_mp + # self.picking_id.carrier = sale_order.carrier + # self.picking_id.address = json_data.get('data', [{}])[0].get('shippingAddressInfo', []).get('fullAddress', []) + # self.picking_id.note_by_buyer = json_data.get('data', [{}])[0].get('extraInfo', []).get('noteByBuyer', []) + + self.execute_status = 'so_confirm' + else: + self.execute_status = 'so_draft' except Exception as e: self.write({ diff --git a/fixco_custom/models/purchase_order.py b/fixco_custom/models/purchase_order.py index 357550a..2dbb3e9 100644 --- a/fixco_custom/models/purchase_order.py +++ b/fixco_custom/models/purchase_order.py @@ -227,13 +227,12 @@ class PurchaseOrder(models.Model): unique_soo = list(set(soo_numbers)) if len(unique_soo) == 1: order.soo_number = unique_soo[0] - if not order.picking_ids.soo_number: - order.picking_ids[0].soo_number = unique_soo[0] + if not order.picking_ids.number_soo: + order.picking_ids[0].number_soo = unique_soo[0] elif len(unique_soo) > 1: order.soo_number = ", ".join(unique_soo) - if not order.picking_ids.soo_number: - order.picking_ids[0].soo_number = ", ".join(unique_soo) - + if not order.picking_ids.number_soo: + order.picking_ids[0].number_soo = ", ".join(unique_soo) else: order.soo_number = False diff --git a/fixco_custom/models/queue_job.py b/fixco_custom/models/queue_job.py new file mode 100644 index 0000000..68ddf8c --- /dev/null +++ b/fixco_custom/models/queue_job.py @@ -0,0 +1,46 @@ +from odoo import models, fields, api +from odoo.exceptions import UserError +import traceback + +class QueueJob(models.Model): + _name = 'queue.job' + _description = 'Queueing Job Runner' + _order = 'create_date desc' + + name = fields.Char(required=True) + model_name = fields.Char(required=True) + method_name = fields.Char(required=True) + res_id = fields.Integer(string='Record ID') + state = fields.Selection([ + ('draft', 'Draft'), + ('running', 'Running'), + ('done', 'Done'), + ('error', 'Error'), + ], default='draft') + + error_message = fields.Text() + + def action_run_selected(self): + for job in self: + job.action_run() + + def action_run(self, limit=10): + jobs = self.search([('state', '=', 'draft')], order='create_date desc', limit=limit) + for job in jobs: + job.state = 'running' + try: + + record = self.env[job.model_name].browse(job.res_id) + if not record.exists(): + raise UserError('Target record not found') + + method = getattr(record, job.method_name, None) + if not method: + raise UserError('Method not found') + + method() # 🔥 EXECUTE + job.state = 'done' + + except Exception as e: + job.state = 'error' + job.error_message = traceback.format_exc() diff --git a/fixco_custom/models/sale.py b/fixco_custom/models/sale.py index b7cbe17..8b04538 100755 --- a/fixco_custom/models/sale.py +++ b/fixco_custom/models/sale.py @@ -19,6 +19,18 @@ class SaleOrder(models.Model): remaining_down_payment = fields.Float('Remaining Down Payment', compute='_compute_remaining_down_payment', store=True) + def create_invoices(self): + created_invoices = self.env['account.move'] + for order in self: + # Create invoice for this SO only + invoice = order.with_context(default_invoice_origin=order.name)._create_invoices(final=True) + invoice.action_post() + created_invoices += invoice + + # Link the invoice to the SO + order.invoice_ids += invoice + + def _compute_remaining_down_payment(self): for order in self: down_payments = self.env['account.move'].search([ diff --git a/fixco_custom/models/sale_order_multi_invoices.py b/fixco_custom/models/sale_order_multi_invoices.py index 5c42dc6..f763313 100644 --- a/fixco_custom/models/sale_order_multi_invoices.py +++ b/fixco_custom/models/sale_order_multi_invoices.py @@ -5,44 +5,55 @@ class SaleOrderMultiInvoices(models.TransientModel): _name = 'sale.order.multi_invoices' _description = 'Create Invoices for Multiple Sales Orders' - def create_invoices(self): - # Get SO IDs from context + def queue_job(self): so_ids = self._context.get('so_ids', []) - if not so_ids: - raise UserError(_("No sales orders selected!")) - - # Browse all selected sales orders sale_orders = self.env['sale.order'].browse(so_ids) - created_invoices = self.env['account.move'] + for sale in sale_orders: + self.env['queue.job'].create({ + 'name': f'Create Invoice {sale.name}', + 'model_name': 'sale.order', + 'method_name': 'create_invoices', + 'res_id': sale.id, + }) + + # def create_invoices(self): + # # Get SO IDs from context + # so_ids = self._context.get('so_ids', []) + # if not so_ids: + # raise UserError(_("No sales orders selected!")) + + # # Browse all selected sales orders + # sale_orders = self.env['sale.order'].browse(so_ids) + # created_invoices = self.env['account.move'] - # Create one invoice per SO (even if partner is the same) - for order in sale_orders: - # Create invoice for this SO only - invoice = order.with_context(default_invoice_origin=order.name)._create_invoices(final=True) - invoice.action_post() - created_invoices += invoice + # # Create one invoice per SO (even if partner is the same) + # for order in sale_orders: + # # Create invoice for this SO only + # invoice = order.with_context(default_invoice_origin=order.name)._create_invoices(final=True) + # invoice.action_post() + # created_invoices += invoice - # Link the invoice to the SO - order.invoice_ids += invoice + # # Link the invoice to the SO + # order.invoice_ids += invoice - # Return action to view created invoices - if len(created_invoices) > 1: - action = { - 'name': _('Created Invoices'), - 'type': 'ir.actions.act_window', - 'res_model': 'account.move', - 'view_mode': 'tree,form', - 'domain': [('id', 'in', created_invoices.ids)], - } - elif created_invoices: - action = { - 'name': _('Created Invoice'), - 'type': 'ir.actions.act_window', - 'res_model': 'account.move', - 'view_mode': 'form', - 'res_id': created_invoices.id, - } - else: - action = {'type': 'ir.actions.act_window_close'} + # # Return action to view created invoices + # if len(created_invoices) > 1: + # action = { + # 'name': _('Created Invoices'), + # 'type': 'ir.actions.act_window', + # 'res_model': 'account.move', + # 'view_mode': 'tree,form', + # 'domain': [('id', 'in', created_invoices.ids)], + # } + # elif created_invoices: + # action = { + # 'name': _('Created Invoice'), + # 'type': 'ir.actions.act_window', + # 'res_model': 'account.move', + # 'view_mode': 'form', + # 'res_id': created_invoices.id, + # } + # else: + # action = {'type': 'ir.actions.act_window_close'} - return action
\ No newline at end of file + # return action
\ No newline at end of file diff --git a/fixco_custom/models/stock_picking.py b/fixco_custom/models/stock_picking.py index 34a818d..35fa046 100755 --- a/fixco_custom/models/stock_picking.py +++ b/fixco_custom/models/stock_picking.py @@ -63,6 +63,7 @@ class StockPicking(models.Model): ginee_task_id = fields.Char("Ginee Task ID", tracking=True) soo_number = fields.Char(string='SOO Altama Number') + number_soo = fields.Char(string='Number SOO Altama') type_sku = fields.Selection([('single', 'Single SKU'), ('multi', 'Multi SKU')], string='Type SKU') list_product = fields.Char(string='List Product') is_dispatched = fields.Boolean(string='Is Dispatched', default=False, compute='_compute_is_dispatched', readonly=True) diff --git a/fixco_custom/models/upload_cancel_picking.py b/fixco_custom/models/upload_cancel_picking.py index a42ef1d..b4038bf 100644 --- a/fixco_custom/models/upload_cancel_picking.py +++ b/fixco_custom/models/upload_cancel_picking.py @@ -144,8 +144,13 @@ class UploadCancelPicking(models.Model): } def action_cancel_picking(self): - self.date_upload = datetime.utcnow() - self.picking_lines.cancel_picking() + for line in self.picking_lines: + self.env['queue.job'].create({ + 'name': f'Cancel Picking {line.name}', + 'model_name': 'upload.cancel.picking.line', + 'method_name': 'cancel_picking', + 'res_id': line.id, + }) class UploadCancelPickingLine(models.Model): @@ -209,5 +214,4 @@ class UploadCancelPickingLine(models.Model): ) % line.invoice_marketplace def cancel_picking(self): - for line in self: - line.picking_id.action_cancel()
\ No newline at end of file + self.picking_id.action_cancel()
\ No newline at end of file diff --git a/fixco_custom/models/upload_ginee.py b/fixco_custom/models/upload_ginee.py index d522787..51ddbd9 100644 --- a/fixco_custom/models/upload_ginee.py +++ b/fixco_custom/models/upload_ginee.py @@ -161,12 +161,20 @@ class UploadGinee(models.Model): self.date_upload = datetime.utcnow() self.ginee_lines.create_so_and_detail_order() - def action_get_order_id_and_create_detail_order(self): - - self.date_upload = datetime.utcnow() - self.ginee_lines.get_order_id() - self.ginee_lines.create_so_and_detail_order() + # def action_get_order_id_and_create_detail_order(self): + # self.date_upload = datetime.utcnow() + # self.ginee_lines.get_order_id() + # self.ginee_lines.create_so_and_detail_order() + def action_get_order_id_and_create_detail_order(self): + for line in self.ginee_lines: + self.env['queue.job'].create({ + 'name': f'Get Order Ginee {line.invoice_marketplace}', + 'model_name': 'upload.ginee.line', + 'method_name': 'get_order_id_and_create_detail_order', + 'res_id': line.id, + }) + class UploadGineeLine(models.Model): _name = "upload.ginee.line" _description = "Upload Ginee Line" @@ -182,6 +190,10 @@ class UploadGineeLine(models.Model): is_grouped = fields.Boolean('Is Grouped', default=False) group_key = fields.Char('Group Key') + def get_order_id_and_create_detail_order(self): + self.get_order_id() + self.create_so_and_detail_order() + def _process_grouped_blibli_orders(self, lines): """Process a group of BLIBLI orders with the same invoice prefix""" order_ids = [line.order_id for line in lines if line.order_id] diff --git a/fixco_custom/security/ir.model.access.csv b/fixco_custom/security/ir.model.access.csv index 7795156..817595b 100755 --- a/fixco_custom/security/ir.model.access.csv +++ b/fixco_custom/security/ir.model.access.csv @@ -44,3 +44,4 @@ access_stock_return_picking,stock.return.picking,model_stock_return_picking,,1,1 access_stock_return_picking_line,stock.return.picking.line,model_stock_return_picking_line,,1,1,1,1 access_upload_cancel_picking,access.upload.cancel.picking,model_upload_cancel_picking,,1,1,1,1 access_upload_cancel_picking_line,access.upload.cancel.picking.line,model_upload_cancel_picking_line,,1,1,1,1 +access_queue_job,access.queue.job,model_queue_job,,1,1,1,1
\ No newline at end of file diff --git a/fixco_custom/views/queue_job.xml b/fixco_custom/views/queue_job.xml new file mode 100644 index 0000000..9934d1b --- /dev/null +++ b/fixco_custom/views/queue_job.xml @@ -0,0 +1,54 @@ +<?xml version="1.0" encoding="UTF-8"?> +<odoo> + <record id="view_queue_job_tree" model="ir.ui.view"> + <field name="name">queue.job.tree</field> + <field name="model">queue.job</field> + <field name="arch" type="xml"> + <tree create="false" edit="false" decoration-success="state == 'done'" + decoration-danger="state == 'error'" + decoration-warning="state == 'running'"> + + <field name="name"/> + <field name="model_name"/> + <field name="method_name"/> + <field name="res_id"/> + <field name="state"/> + + <button name="action_run" + type="object" + string="RUN" + class="btn-primary" + attrs="{'invisible': [('state', '=', 'done')]}"/> + + </tree> + </field> + </record> + + <record id="action_run_selected_queue_job" model="ir.actions.server"> + <field name="name">Execute Selected Jobs</field> + <field name="model_id" ref="model_queue_job"/> + <field name="binding_model_id" ref="model_queue_job"/> + <field name="binding_type">action</field> + <field name="state">code</field> + <field name="code"> + records.action_run_selected() + </field> + </record> + + + + <record id="queue_job_action" model="ir.actions.act_window"> + <field name="name">Queue Job</field> + <field name="type">ir.actions.act_window</field> + <field name="res_model">queue.job</field> + <field name="view_mode">tree,form</field> + </record> + + <menuitem + action="queue_job_action" + id="queue_job" + parent="base.menu_users" + name="Queue Job" + sequence="1" + /> +</odoo> diff --git a/fixco_custom/views/sale_order_multi_invoices.xml b/fixco_custom/views/sale_order_multi_invoices.xml index 0f8087c..1e9c24a 100644 --- a/fixco_custom/views/sale_order_multi_invoices.xml +++ b/fixco_custom/views/sale_order_multi_invoices.xml @@ -16,7 +16,7 @@ </div> </sheet> <footer> - <button name="create_invoices" string="Create Invoices" type="object" + <button name="queue_job" string="Create Invoices" type="object" class="btn-primary" default_focus="1"/> <button string="Cancel" class="btn-secondary" special="cancel"/> </footer> diff --git a/fixco_custom/views/stock_picking.xml b/fixco_custom/views/stock_picking.xml index b7fb001..875e50a 100755 --- a/fixco_custom/views/stock_picking.xml +++ b/fixco_custom/views/stock_picking.xml @@ -72,7 +72,7 @@ <field name="location_id" position="after"> <field name="carrier"/> <field name="shipment_group_id"/> - <field name="soo_number"/> + <field name="number_soo"/> <field name="type_sku"/> </field> |
