From 107c3aee72d71446b9ca9374f08292cb3e2e1ae7 Mon Sep 17 00:00:00 2001 From: "DESKTOP-IQ6LM3K\\Jose Antonio" Date: Mon, 20 May 2024 20:16:17 -0600 Subject: [PATCH] [IMP] api: Code improvements and restructuring - Moved the logic of retrieving shipping information from the controller to the sale.order model to improve code maintainability. - Added the method get_shipping_info in the sale_order.py model to handle the retrieval of detailed shipping information, including address and picking data. Updated the controller to call the new model method, ensuring a cleaner and more modular design. - Added a route to retrieve states by country, which enhances the flexibility of the application in handling different countries dynamically. Implemented the get_inventory and get_inventory_by_sku methods in the controller to provide comprehensive inventory data based on SKUs. - Ensured that __init__.py and __manifest__ files include EOF. - Get the sales order through the sales order model. - Combined get_inventory and get_inventory_by_sku endpoints into a single endpoint in the controller. This unified approach simplifies the API and improves code maintainability. Implemented logic to handle both GET (all products with SKUs) and POST (specific product by SKU) requests in the same method. - Refactored the endpoint for scheduling activities related to invoices to use the invoice.activity_schedule method. These changes improve the modularity, readability, and maintainability of the codebase, making it easier to manage and extend in the future. Closes #25 --- hantec_api_ecommerce/__init__.py | 3 +- hantec_api_ecommerce/__manifest__.py | 2 +- hantec_api_ecommerce/controllers/__init__.py | 2 +- hantec_api_ecommerce/controllers/main.py | 305 ++++++------------- hantec_api_ecommerce/models/__init__.py | 2 + hantec_api_ecommerce/models/sale_order.py | 67 ++++ 6 files changed, 160 insertions(+), 221 deletions(-) create mode 100644 hantec_api_ecommerce/models/__init__.py create mode 100644 hantec_api_ecommerce/models/sale_order.py diff --git a/hantec_api_ecommerce/__init__.py b/hantec_api_ecommerce/__init__.py index 15c7016..7cdad7f 100644 --- a/hantec_api_ecommerce/__init__.py +++ b/hantec_api_ecommerce/__init__.py @@ -1,2 +1,3 @@ -from . import controllers \ No newline at end of file +from . import models +from . import controllers diff --git a/hantec_api_ecommerce/__manifest__.py b/hantec_api_ecommerce/__manifest__.py index 2336285..622fb3e 100644 --- a/hantec_api_ecommerce/__manifest__.py +++ b/hantec_api_ecommerce/__manifest__.py @@ -9,4 +9,4 @@ "installable": True, "development_status": "Production/Stable", "maintainers": ["C&O PROJECTS AND SOLUTIONS"], -} \ No newline at end of file +} diff --git a/hantec_api_ecommerce/controllers/__init__.py b/hantec_api_ecommerce/controllers/__init__.py index ab929af..8b6d05e 100644 --- a/hantec_api_ecommerce/controllers/__init__.py +++ b/hantec_api_ecommerce/controllers/__init__.py @@ -1,2 +1,2 @@ -from . import main \ No newline at end of file +from . import main diff --git a/hantec_api_ecommerce/controllers/main.py b/hantec_api_ecommerce/controllers/main.py index a292d50..2c2b9d2 100644 --- a/hantec_api_ecommerce/controllers/main.py +++ b/hantec_api_ecommerce/controllers/main.py @@ -7,8 +7,7 @@ class MainController(Controller): @route("/create_contact", methods=["POST"], type="json", auth="user") def create_contact(self): - """ - Combines the logic of searching and creating contacts based on email, phone, or store name. + """Combines the logic of searching and creating contacts based on email, phone, or store name. This function first checks if a contact already exists based on the provided email, phone, or store name. If a matching contact is found, it returns the contact ID. @@ -44,9 +43,9 @@ def create_contact(self): domain = [] if email_prefix: - domain.append(("email", "ilike", f"{email_prefix}%")) + domain.append(("email", "=", f"{email_prefix}%")) if phone_suffix: - domain.append(("phone", "ilike", f"%{phone_suffix}")) + domain.append(("phone", "=", f"%{phone_suffix}")) existing_contact = env["res.partner"].search(domain, limit=1) @@ -65,7 +64,7 @@ def create_contact(self): if store_name: store_name_suffix = store_name[len(store_name) - 4 :] - domain = [("name", "ilike", f"%{store_name_suffix}")] + domain = [("name", "=", f"%{store_name_suffix}")] existing_contact = env["res.partner"].search(domain, limit=1) if existing_contact: @@ -86,8 +85,7 @@ def create_contact(self): @route("/update_contact", methods=["POST"], type="json", auth="user") def update_contact(self): - """ - Updates a contact with new values. + """Updates a contact with new values. This function updates the details of a specified contact using the values provided in the JSON request body. @@ -119,8 +117,7 @@ def update_contact(self): @route("/address_invoice", methods=["POST"], type="json", auth="user") def create_address_invoice(self): - """ - Creates or updates the billing address for a given partner. + """Creates or updates the billing address for a given partner. This function creates or updates the billing address of a specified partner using the details provided in the JSON request body. @@ -164,8 +161,7 @@ def create_address_invoice(self): @route("/delivery_address", methods=["POST"], type="json", auth="user") def delivery_address(self): - """ - Creates or updates the delivery address for a given partner. + """Creates or updates the delivery address for a given partner. This function creates or updates the delivery address of a specified partner using the details provided in the JSON request body. @@ -214,8 +210,7 @@ def delivery_address(self): @route("/create_sale_order", methods=["POST"], type="json", auth="user") def create_sale_order(self): - """ - Creates a sale order. + """Creates a sale order. This function creates a sale order using the details provided in the JSON request body. The required fields include the customer ID and a list of product lines. @@ -312,8 +307,7 @@ def create_sale_order(self): @route("/update_sale_order", methods=["POST"], type="json", auth="user") def update_sale_order(self): - """ - Updates a sale order with a tracking number. + """Updates a sale order with a tracking number. This function updates the tracking number of a specified sale order using the details provided in the JSON request body. @@ -347,11 +341,10 @@ def update_sale_order(self): auth="user", ) def invoice_sale_order(self, order=False): - """ - Creates an invoice for a sale order. + """Creates an invoice for a sale order. - This function creates an invoice for a specified sale order using the details - provided in the JSON request body. + This function creates an invoice for a specified sale order by its model + passed in the URL, and creates the invoice order. URL parameter: - order (sale.order): The sale order model instance. @@ -401,8 +394,7 @@ def invoice_sale_order(self, order=False): auth="user", ) def register_payment(self, invoice=False): - """ - Registers a payment for an invoice. + """Registers a payment for an invoice. This function registers a payment for a specified invoice using the details provided in the JSON request body. @@ -443,17 +435,11 @@ def register_payment(self, invoice=False): # Register the payment wizard.action_create_payments() - return {"success": "The payment has been successfully registered."} + return {"success": "The payment has been successfully registered."} - @route( - '/get_shipping_info/', - methods=["GET"], - type="json", - auth="user", - ) - def get_shipping_info(self, order=False): - """ - Retrieves shipping information for a given sale order. + @route('/get_shipping_info/', methods=["GET"], type="json", auth="user") + def get_shipping_info(self, order): + """Retrieves shipping information for a given sale order. This function gathers shipping information related to the specified sale order, including delivery address details and shipping records. @@ -467,76 +453,14 @@ def get_shipping_info(self, order=False): Returns: dict: A dictionary with a success message and the shipping data. - """ - # Delivery address data - shipping_address = order.partner_shipping_id - address_data = { - "name": shipping_address.name, - "phone": shipping_address.phone, - "email": shipping_address.email, - "street": shipping_address.street, - "street2": shipping_address.street2, - "city": shipping_address.city, - "state": ( - shipping_address.state_id.name if shipping_address.state_id.name else "" - ), - "zip": shipping_address.zip, - "country": ( - shipping_address.country_id.name if shipping_address.country_id else "" - ), - } - - # Get related shipping records - pickings = order.picking_ids - - # Prepare shipping data for the response - shipping_data = [] - last_picking = None - for picking in pickings: - lines_data = [ - { - "product": line.product_id.name, - "quantity": line.product_uom_qty, - "done": line.quantity_done, - "name": line.name, - } - for line in picking.move_lines - ] - - shipping_info = { - "name": picking.name, - "scheduled_date": picking.scheduled_date, - "state": picking.state, - "carrier": ( - picking.carrier_id.name if picking.carrier_id else "Not defined" - ), - "tracking_reference": picking.carrier_tracking_ref, - "lines": lines_data, - "address_data": address_data, - } - shipping_data.append(shipping_info) - last_picking = picking - - if last_picking: - shipping_data.append( - { - "sales_team": order.team_id.id, - "market_place_reference": order.channel_order_reference, - "sale_order_name": order.name, - "picking_id": last_picking.id, - "picking_name": last_picking.name, - } - ) - - return {"message": "Shipping data retrieved", "shipping_data": shipping_data} + return order.get_shipping_info() @route( "/download_invoice/", methods=["GET"], type="http", auth="user" ) def download_invoice(self, invoice_id): - """ - Downloads an invoice as a PDF. + """Downloads an invoice as a PDF. This function generates and downloads a PDF file for the invoice identified by its ID. @@ -552,7 +476,7 @@ def download_invoice(self, invoice_id): """ # Find the invoice - invoice = request.env["account.move"].browse(invoice_id) + invoice = request.env["account.move"].browse(invoice_id).exists() # Generate the PDF pdf_content, _ = request.env.ref("account.account_invoices")._render_qweb_pdf( @@ -568,8 +492,7 @@ def download_invoice(self, invoice_id): "/stamp_invoice/", methods=["POST"], auth="user", type="json" ) def stamp_invoice(self, invoice_id=None): - """ - Stamps an invoice. + """Stamps an invoice. This function attempts to stamp an invoice identified by its ID. If the invoice is already stamped, it returns the UUID of the stamped invoice. Otherwise, it @@ -594,7 +517,7 @@ def stamp_invoice(self, invoice_id=None): """ # Find the invoice by its ID - invoice = request.env["account.move"].browse(invoice_id) + invoice = request.env["account.move"].browse(invoice_id).exists() # Check if the invoice is already stamped if invoice.l10n_mx_edi_cfdi_uuid: @@ -627,8 +550,7 @@ def stamp_invoice(self, invoice_id=None): methods=["POST"], ) def send_invoice_by_email(self, invoice_id=False): - """ - Sends an invoice by email. + """Sends an invoice by email. This function sends an invoice identified by its ID via email, using the invoice sending wizard in Odoo. @@ -643,7 +565,7 @@ def send_invoice_by_email(self, invoice_id=False): dict: A dictionary with a success message. """ - invoice = request.env["account.move"].browse(invoice_id) + invoice = request.env["account.move"].browse(invoice_id).exists() action = invoice.action_invoice_sent() action_context = action["context"] invoice_send_wizard = ( @@ -657,16 +579,15 @@ def send_invoice_by_email(self, invoice_id=False): return {"success": "The invoice has been successfully sent."} - @route("/confirm_sale_order", methods=["POST"], type="json", auth="user") - def confirm_sale_order(self): - """ - Confirms a sale order. + @route('/confirm_sale_order/', methods=["POST"], type="json", auth="user") + def confirm_sale_order(self, order=False): + """Confirms a sale order. - This function confirms a sale order identified by its ID, using the details - provided in the JSON request body. + This function confirms a sale order identified by its model instance + passed in the URL, and confirms the order. - JSON request body: - - sale_order_id (int): The ID of the sale order to be confirmed. + URL parameter: + - order (sale.order): The sale order model instance. JSON response: - message (str): A confirmation message indicating the action performed. @@ -674,25 +595,18 @@ def confirm_sale_order(self): Returns: dict: A dictionary with a confirmation message. - """ - data = request.jsonrequest - sale_order_id = data.get("sale_order_id") - - # Find the sale order by its ID - sale_order = request.env["sale.order"].browse(sale_order_id) - + """ # Confirm the sale order - sale_order.action_confirm() - logger.info("Sale order confirmed with ID %s", sale_order_id) + order.action_confirm() + logger.info("Sale order confirmed with ID %s", order.id) return { - "message": f"Sale order with ID: {sale_order_id} successfully confirmed." + "message": f"Sale order with ID: {order.id} successfully confirmed." } @route("/create_schedule_activity", methods=["POST"], type="json", auth="user") def create_schedule_activity(self): - """ - Creates a scheduled activity associated with an existing sale order. + """Creates a scheduled activity associated with an existing sale order. This function creates a scheduled activity in Odoo related to a specific sale order, using the details provided in the JSON request body. @@ -731,22 +645,16 @@ def create_schedule_activity(self): "summary": summary, } ) - logger.info( - "Scheduled activity created with ID %s for sale order %s", - activity.id, - sale_order_id, - ) return { "message": f"Scheduled activity created with ID: {activity.id} for sale order {sale_order_id}." - } - + } + @route( "/create_schedule_activity_invoice", methods=["POST"], type="json", auth="user" ) def create_schedule_activity_invoice(self): - """ - Creates a scheduled activity associated with an existing invoice. + """Creates a scheduled activity associated with an existing invoice. This function creates a scheduled activity in Odoo related to a specific invoice, using the details provided in the JSON request body. @@ -773,38 +681,33 @@ def create_schedule_activity_invoice(self): note = data.get("note", "") # Note for the activity (optional) user_id = data.get("user_id", request.env.uid) # User assigned to the activity - # Create the scheduled activity - activity = request.env["mail.activity"].create( - { - "activity_type_id": activity_type_id, - "note": note, - "date_deadline": date_deadline, - "res_model_id": request.env["ir.model"]._get("account.move").id, - "res_id": invoice_id, - "user_id": user_id, - "summary": summary, - } - ) - logger.info( - "Scheduled activity created with ID %s for invoice %s", - activity.id, - invoice_id, + # Find the invoice + invoice = request.env["account.move"].browse(invoice_id).exists() + + # Schedule the activity + activity = invoice.activity_schedule( + activity_type_id=activity_type_id, + summary=summary, + note=note, + user_id=user_id, + date_deadline=date_deadline, ) return { "message": f"Scheduled activity created with ID: {activity.id} for invoice {invoice_id}." } - @route("/send_message_sale_order", methods=["POST"], type="json", auth="user") - def send_message_sale_order(self): - """ - Sends a message to a specific sale order. + @route('/send_message_sale_order/', methods=["POST"], type="json", auth="user") + def send_message_sale_order(self, order=False): + """Sends a message to a specific sale order. - This function posts a message to the chatter of a sale order identified by its ID, - using the message body provided in the JSON request body. + This function posts a message to the chatter of a sale order identified by its model + passed in the URL, and post the message in sale order + + URL parameter: + - order (sale.order): The sale order model instance. JSON request body: - - sale_order_id (int): The ID of the sale order. - message_body (str): The content of the message to be posted. JSON response: @@ -814,28 +717,24 @@ def send_message_sale_order(self): dict: A dictionary with a confirmation message. """ - data = request.jsonrequest - sale_order_id = data.get("sale_order_id") - message_body = data.get("message_body") - - # Find the sale order by its ID - sale_order = request.env["sale.order"].browse(sale_order_id) - + message_body = request.jsonrequest.get("message_body") # Post the message in the sale order chatter - sale_order.message_post(body=message_body) - logger.info("Message posted in sale order with ID %s", sale_order_id) + order.message_post(body=message_body) + logger.debug("Message posted in sale order with ID %s", order.id) return { - "message": f"Message successfully posted in sale order with ID: {sale_order_id}." + "message": f"Message successfully posted in sale order with ID: {order.id}." } - - @route("/get_inventory", methods=["GET"], type="json", auth="user") + + @route(["/get_inventory", "/get_inventory_by_sku"], methods=["POST", "GET"], type="json", auth="user") def get_inventory(self): - """ - Retrieves the inventory details for all products with a SKU. + """Retrieves the inventory details for all products with a SKU or a specific product based on its SKU. + + This function searches for all products in the Odoo database that have a SKU (default_code not null) + or searches for a product using the provided SKU and returns their inventory details in a JSON response. - This function searches for all products in the Odoo database that have a SKU - (default_code not null) and returns their inventory details in a JSON response. + JSON request body for POST (if applicable): + - sku (str, optional): The SKU of the product to search for. JSON response: - message (str): A message indicating the action performed. @@ -848,9 +747,11 @@ def get_inventory(self): Returns: dict: A dictionary with a message and the inventory details of the products. """ - products = request.env["product.product"].search( - [("default_code", "!=", False)] - ) + if request.httprequest.method == 'POST': + sku = request.jsonrequest.get("sku") + products = request.env["product.product"].search([("default_code", "=", sku)]) + else: + products = request.env["product.product"].search([("default_code", "!=", False)]) product_fields = ["name", "default_code", "qty_available", "virtual_available"] inventory_list = products.read(product_fields) @@ -858,58 +759,26 @@ def get_inventory(self): return { "message": "Inventory data retrieved", "inventory_data": inventory_list, - } + } + + @route('/get_states/', methods=["GET"], type="json", auth="user") + def get_states(self, country): + """Retrieves the list of states in the specified country. - @route("/get_inventory_by_sku", methods=["POST"], type="json", auth="user") - def get_inventory_by_sku(self): - """ - Retrieves the inventory details for a product based on its SKU. - - This function searches for a product in the Odoo database using the provided SKU - and returns the inventory details in a JSON response. - - JSON request body: - - sku (str): The SKU of the product to search for. - - JSON response: - - message (str): A message indicating the action performed. - - inventory_data (list of dict): A list of dictionaries containing the product's inventory details, including: - - name (str): The name of the product. - - default_code (str): The SKU of the product. - - qty_available (float): The quantity of the product available. - - virtual_available (float): The virtual quantity of the product available. - - Returns: - dict: A dictionary with a message and the inventory details of the product. - """ - sku = request.jsonrequest.get("sku") - product = request.env["product.product"].search([("default_code", "=", sku)]) - product_fields = ["name", "default_code", "qty_available", "virtual_available"] - data_product = product.read(product_fields) - - return { - "message": "Inventory data retrieved", - "inventory_data": data_product, - } - - @route("/get_states_mexico", methods=["GET"], type="json", auth="user") - def get_states_mexico(self): - """ - Retrieves the list of states in Mexico. - - This function retrieves the list of states associated with Mexico in the Odoo database + This function retrieves the list of states associated with the given country in the Odoo database and returns them in a JSON response. + URL parameter: + - country (res.country): The country model instance. + JSON response: - message (str): A message indicating the action performed. - - states_list (list of dict): A list of dictionaries containing the names of the states. + - states_list (list of dict): A list of dictionaries containing the IDs, names, and codes of the states. Returns: dict: A dictionary with a message and the list of states. """ - env = request.env - mexico = env.ref("base.mx") - states = mexico.state_ids - states_list = states.read(["name"]) + states = country.state_ids + states_list = states.read(["name", "code"]) - return {"message": "List states from Mexico", "states_list": states_list} + return {"message": f"List states from {country.name}", "states_list": states_list} diff --git a/hantec_api_ecommerce/models/__init__.py b/hantec_api_ecommerce/models/__init__.py new file mode 100644 index 0000000..9a3f5c8 --- /dev/null +++ b/hantec_api_ecommerce/models/__init__.py @@ -0,0 +1,2 @@ + +from . import sale_order diff --git a/hantec_api_ecommerce/models/sale_order.py b/hantec_api_ecommerce/models/sale_order.py new file mode 100644 index 0000000..0a8c603 --- /dev/null +++ b/hantec_api_ecommerce/models/sale_order.py @@ -0,0 +1,67 @@ + +from odoo import models, fields + +class SaleOrder(models.Model): + _inherit = 'sale.order' + + def get_shipping_info(self): + """Retrieve shipping information for the sale order instance. + + This method gathers shipping information related to the sale order, including + delivery address details and shipping records. + + Returns: + dict: A dictionary containing the shipping information. + """ + + # Delivery address data + shipping_address = self.partner_shipping_id + fields = ["name", "phone", "email", "street", "street2", "city", "state_id", "zip", "country_id"] + address_data = shipping_address.read(fields)[0] if shipping_address else {} + + address_data.update({ + "state": shipping_address.state_id.name if shipping_address.state_id else "", + "country": shipping_address.country_id.name if shipping_address.country_id else "" + }) + + # Get related shipping records + pickings = self.picking_ids + + # Prepare shipping data for the response + shipping_data = [] + last_picking = None + for picking in pickings: + lines_data = [ + { + "product": line.product_id.name, + "quantity": line.product_uom_qty, + "done": line.quantity_done, + "name": line.name, + } + for line in picking.move_lines + ] + + shipping_info = { + "name": picking.name, + "scheduled_date": picking.scheduled_date, + "state": picking.state, + "carrier": picking.carrier_id.name if picking.carrier_id else "Not defined", + "tracking_reference": picking.carrier_tracking_ref, + "lines": lines_data, + "address_data": address_data, + } + shipping_data.append(shipping_info) + last_picking = picking + + if last_picking: + shipping_data.append( + { + "sales_team": self.team_id.id, + "market_place_reference": self.channel_order_reference, + "sale_order_name": self.name, + "picking_id": last_picking.id, + "picking_name": last_picking.name, + } + ) + + return {"message": "Shipping data retrieved", "shipping_data": shipping_data}