diff options
| author | Indoteknik . <it@fixcomart.co.id> | 2025-08-09 14:45:48 +0700 |
|---|---|---|
| committer | Indoteknik . <it@fixcomart.co.id> | 2025-08-09 14:45:48 +0700 |
| commit | 979ffc90fffe2f09788016376d71a940a28f8fed (patch) | |
| tree | a5408296eb204a0a5066578664ed0acb13174f27 /indoteknik_api/controllers | |
| parent | 33621956bdb9d807b480eda44ce7f2152f508695 (diff) | |
| parent | df5ff0fc9bcd45c19b74288d8e5cfee018ba5bdd (diff) | |
Merge branch 'odoo-backup' of https://bitbucket.org/altafixco/indoteknik-addons into pum-v2
Diffstat (limited to 'indoteknik_api/controllers')
| -rw-r--r-- | indoteknik_api/controllers/api_v1/sale_order.py | 440 | ||||
| -rw-r--r-- | indoteknik_api/controllers/api_v1/stock_picking.py | 17 | ||||
| -rw-r--r-- | indoteknik_api/controllers/api_v1/voucher.py | 2 |
3 files changed, 312 insertions, 147 deletions
diff --git a/indoteknik_api/controllers/api_v1/sale_order.py b/indoteknik_api/controllers/api_v1/sale_order.py index e1c643e5..6f5a3d44 100644 --- a/indoteknik_api/controllers/api_v1/sale_order.py +++ b/indoteknik_api/controllers/api_v1/sale_order.py @@ -1,8 +1,11 @@ from .. import controller -from odoo import http +from odoo import http, fields from datetime import datetime, timedelta from odoo.http import request import json +import logging + +_logger = logging.getLogger(__name__) class SaleOrder(controller.Controller): @@ -100,7 +103,11 @@ class SaleOrder(controller.Controller): 'site': [], 'limit': ['default:0', 'number'], 'offset': ['default:0', 'number'], - 'context': [] + 'context': [], + 'status': [], + 'sort': [], + 'startDate': [], + 'endDate': [], }) limit = params['value']['limit'] offset = params['value']['offset'] @@ -120,10 +127,19 @@ class SaleOrder(controller.Controller): if params['value']['name']: name = params['value']['name'].replace(' ', '%') - domain += [ + order_lines = request.env['sale.order.line'].search([ + ('order_id.partner_id', 'in', partner_child_ids), '|', - ('name', 'ilike', '%' + name + '%'), - ('partner_purchase_order_name', 'ilike', '%' + name + '%') + ('product_id.name', 'ilike', name), + ('product_id.default_code', 'ilike', name), + ]) + + sale_order_ids_from_lines = order_lines.mapped('order_id.id') + + domain += ['|', '|', + ('name', 'ilike', name), + ('partner_purchase_order_name', 'ilike', name), + ('id', 'in', sale_order_ids_from_lines) ] if params['value']['site']: @@ -132,11 +148,86 @@ class SaleOrder(controller.Controller): ('partner_id.site_id.name', 'ilike', '%' + site + '%') ] + status = params['value'].get('status') + if status: + if status == 'quotation': + domain += [('state', '=', 'draft')] + domain += [('approval_status', '=', False)] + + elif status == 'cancel': + domain += [('state', '=', 'cancel')] + + elif status == 'diproses': + domain += [ + ('state', '=', 'draft'), + ('approval_status', 'in', ['pengajuan1', 'pengajuan2']), + ] + + elif status in ['dikemas', 'dikirim', 'selesai', 'partial']: + domain += [('state', '=', 'sale')] + + elif status == 'all': + domain += [] + + # Sorting + order = None + if params['value']['sort']: + if params['value']['sort'] == 'asc': + order = 'amount_total asc' + elif params['value']['sort'] == 'desc': + order = 'amount_total desc' + + # Filter berdasarkan tanggal order + try: + if params['value']['startDate'] and params['value']['endDate']: + start_date = datetime.strptime(params['value']['startDate'], '%d/%m/%Y').strftime('%Y-%m-%d 00:00:00') + end_date = datetime.strptime(params['value']['endDate'], '%d/%m/%Y').strftime('%Y-%m-%d 23:59:59') + else: + start_date = '2023-01-01 00:00:00' + end_date = fields.Datetime.now().strftime('%Y-%m-%d 23:59:59') + + domain.append(('date_order', '>=', start_date)) + domain.append(('date_order', '<=', end_date)) + + except ValueError: + return self.response(code=400, description="Invalid date format. Use 'DD/MM/YYYY'.") + + + sale_orders = request.env['sale.order'].search( - domain, offset=offset, limit=limit) + domain, order=order) + status = params['value'].get('status') + if status in ['dikemas', 'dikirim', 'selesai', 'partial']: + filtered_orders = [] + for sale_order in sale_orders: + bu_pickings = [ + p for p in sale_order.picking_ids + if p.picking_type_id and p.picking_type_id.id == 29 + ] + total = len(bu_pickings) + done_pickings = [p for p in bu_pickings if p.state == 'done'] + done_with_driver = [p for p in done_pickings if p.sj_return_date] + done_without_driver = [p for p in done_pickings if not p.sj_return_date] + + if status == 'dikemas' and len(done_pickings) == 0: + filtered_orders.append(sale_order) + elif status == 'dikirim' and len(done_pickings) == total and len(done_pickings) > 0 and len(done_without_driver) == total: + filtered_orders.append(sale_order) + elif status == 'selesai' and len(done_pickings) == total and len(done_pickings) > 0 and len(done_with_driver) == total: + filtered_orders.append(sale_order) + elif status == 'partial' and ( + len(done_pickings) != total or + (done_with_driver and done_without_driver) + ): + filtered_orders.append(sale_order) + else: + filtered_orders = sale_orders + + filtered_orders_paginated = filtered_orders[offset: offset + limit] + data = { - 'sale_order_total': request.env['sale.order'].search_count(domain), - 'sale_orders': [request.env['sale.order'].api_v1_single_response(x) for x in sale_orders] + 'sale_order_total': len(filtered_orders), + 'sale_orders': [request.env['sale.order'].api_v1_single_response(x) for x in filtered_orders_paginated] } return self.response(data) @@ -146,7 +237,7 @@ class SaleOrder(controller.Controller): def partner_get_sale_order_detail(self, **kw): params = self.get_request_params(kw, { 'partner_id': ['number'], - 'id': ['number'] + 'id': ['number'], }) if not params['valid']: return self.response(code=400, description=params) @@ -333,8 +424,8 @@ class SaleOrder(controller.Controller): return self.response('Unauthorized') sale_order = request.env['sale.order'].sudo().search_read([('id', '=', id)], ['name']) - pdf, type = request.env['ir.actions.report'].sudo().search([('report_name', '=', 'quotation_so_new')]).render_jasper([id], {}) - # pdf, type = request.env['ir.actions.report'].sudo().search([('report_name', '=', 'indoteknik_custom.report_saleorder_website')])._render_qweb_pdf([id]) + # pdf, type = request.env['ir.actions.report'].sudo().search([('report_name', '=', 'quotation_so_new')]).render_jasper([id], {}) + pdf, type = request.env['ir.actions.report'].sudo().search([('report_name', '=', 'indoteknik_custom.report_saleorder_website')])._render_qweb_pdf([id]) if pdf and len(sale_order) > 0: return rest_api.response_attachment({ @@ -390,138 +481,207 @@ class SaleOrder(controller.Controller): @http.route(PREFIX_PARTNER + 'sale_order/checkout', auth='public', method=['POST', 'OPTIONS'], csrf=False) @controller.Controller.must_authorized() def create_partner_sale_order(self, **kw): - config = request.env['ir.config_parameter'] - product_pricelist_default_discount_id = int(config.get_param('product.pricelist.tier1_v2')) - user_pricelist = request.env.context.get('user_pricelist').id or False + _logger.info("=== START CREATE PARTNER SALE ORDER ===") + + try: + config = request.env['ir.config_parameter'] + product_pricelist_default_discount_id = int(config.get_param('product.pricelist.tier1_v2')) + user_pricelist = request.env.context.get('user_pricelist').id or False + _logger.info( + f"Config - Default Pricelist: {product_pricelist_default_discount_id}, User Pricelist: {user_pricelist}") + + params = self.get_request_params(kw, { + 'user_id': ['number'], + 'partner_id': ['number'], + 'partner_shipping_id': ['required', 'number'], + 'partner_invoice_id': ['required', 'number'], + 'order_line': ['required', 'default:[]'], + 'po_number': [], + 'po_file': [], + 'type': [], + 'delivery_amount': ['number', 'default:0'], + 'carrier_id': [], + 'delivery_service_type': [], + 'flash_sale': ['boolean'], + 'note_website': [], + 'voucher': [], + 'source': [], + 'estimated_arrival_days': ['number', 'default:0'], + 'estimated_arrival_days_start': ['number', 'default:0'] + }) - params = self.get_request_params(kw, { - 'user_id': ['number'], - 'partner_id': ['number'], - 'partner_shipping_id': ['required', 'number'], - 'partner_invoice_id': ['required', 'number'], - 'order_line': ['required', 'default:[]'], - 'po_number': [], - 'po_file': [], - 'type': [], - 'delivery_amount': ['number', 'default:0'], - 'carrier_id': [], - 'delivery_service_type': [], - 'flash_sale': ['boolean'], - 'note_website': [], - 'voucher': [], - 'source': [], - 'estimated_arrival_days': ['number', 'default:0'], - 'estimated_arrival_days_start': ['number', 'default:0'] - }) - - if not params['valid']: - return self.response(code=400, description=params) + _logger.info(f"Raw input params: {kw}") + _logger.info(f"Processed params: {params}") - # Fetch partner details - sales_partner = request.env['res.partner'].browse(params['value']['partner_id']) - partner_invoice = request.env['res.partner'].browse(params['value']['partner_invoice_id']) - main_partner = partner_invoice.get_main_parent() - parameters = { - 'warehouse_id': 8, - 'carrier_id': 1, - 'sales_tax_id': 23, - 'pricelist_id': user_pricelist or product_pricelist_default_discount_id, - 'payment_term_id': 26, - 'team_id': 2, - 'company_id': 1, - 'currency_id': 12, - 'source_id': 59, - 'state': 'draft', - 'picking_policy': 'direct', - 'partner_id': params['value']['partner_id'], - 'partner_shipping_id': params['value']['partner_shipping_id'], - 'real_shipping_id': params['value']['partner_shipping_id'], - 'partner_invoice_id': main_partner.id, - 'real_invoice_id': params['value']['partner_invoice_id'], - 'partner_purchase_order_name': params['value']['po_number'], - 'partner_purchase_order_file': params['value']['po_file'], - 'delivery_amt': params['value']['delivery_amount'] * 1.10, - 'estimated_arrival_days': params['value']['estimated_arrival_days'], - 'estimated_arrival_days_start': params['value']['estimated_arrival_days_start'], - 'shipping_cost_covered': 'customer', - 'shipping_paid_by': 'customer', - 'carrier_id': params['value']['carrier_id'], - 'delivery_service_type': params['value']['delivery_service_type'], - 'flash_sale': params['value']['flash_sale'], - 'note_website': params['value']['note_website'], - 'customer_type': sales_partner.customer_type if sales_partner else 'nonpkp', # Get Customer Type from partner - 'npwp': sales_partner.npwp or '0', # Get NPWP from partner - 'sppkp': sales_partner.sppkp, # Get SPPKP from partner - 'email': sales_partner.email, # Get Email from partner - 'user_id': 11314 # User ID: Boy Revandi - } - - sales_partner = request.env['res.partner'].browse(parameters['partner_id']) - if sales_partner and sales_partner.user_id and sales_partner.user_id.id not in [25]: # 25: System - parameters['user_id'] = sales_partner.user_id.id - - if params['value']['type'] == 'sale_order': - parameters['approval_status'] = 'pengajuan1' - sale_order = request.env['sale.order'].with_context(from_website_checkout=True).create([parameters]) - sale_order.onchange_partner_contact() - - user_id = params['value']['user_id'] - user_cart = request.env['website.user.cart'] - source = params['value']['source'] - carts = user_cart.get_product_by_user(user_id=user_id, selected=True, source=source) - - promotions = [] - for cart in carts: - if cart['cart_type'] == 'product': - order_line = request.env['sale.order.line'].create({ - 'company_id': 1, - 'order_id': sale_order.id, - 'product_id': cart['id'], - 'product_uom_qty': cart['quantity'], - 'product_available_quantity': cart['available_quantity'] - }) - order_line.product_id_change() - order_line.weight = order_line.product_id.weight - order_line.onchange_vendor_id() - order_line.price_unit = cart['price']['price'] - order_line.discount = cart['price']['discount_percentage'] - elif cart['cart_type'] == 'promotion': - promotions.append({ - 'order_id': sale_order.id, - 'program_line_id': cart['id'], - 'quantity': cart['quantity'] - }) - - sale_order._compute_etrts_date() + if not params['valid']: + _logger.error(f"Invalid params: {params}") + return self.response(code=400, description=params) - request.env['sale.order.promotion'].create(promotions) - - if len(promotions) > 0: - sale_order.apply_promotion_program() - sale_order.add_free_product(promotions) - - voucher_code = params['value']['voucher'] - voucher = request.env['voucher'].search([('code', '=', voucher_code),('apply_type', 'in', ['all', 'brand'])], limit=1) - voucher_shipping = request.env['voucher'].search([('code', '=', voucher_code),('apply_type', 'in', ['shipping'])], limit=1) - if voucher and len(promotions) == 0: - sale_order.voucher_id = voucher.id - sale_order.apply_voucher() - - if voucher_shipping and len(promotions) == 0: - sale_order.voucher_shipping_id = voucher_shipping.id - sale_order.apply_voucher_shipping() - - cart_ids = [x['cart_id'] for x in carts] - if sale_order._requires_approval_margin_leader(): #jika ada error tambahkan kondisi if params['value']['type'] == 'sale_order': - sale_order.approval_status = 'pengajuan2' - elif sale_order._requires_approval_margin_manager(): - sale_order.approval_status = 'pengajuan1' - # user_cart.browse(cart_ids).unlink() - sale_order._auto_set_shipping_from_website() - return self.response({ - 'id': sale_order.id, - 'name': sale_order.name - }) + # Fetch partner details + sales_partner = request.env['res.partner'].browse(params['value']['partner_id']) + partner_invoice = request.env['res.partner'].browse(params['value']['partner_invoice_id']) + main_partner = partner_invoice.get_main_parent() + _logger.info( + f"Partner Info - Sales: {sales_partner.id}, Invoice: {partner_invoice.id}, Main: {main_partner.id}") + + parameters = { + 'warehouse_id': 8, + 'carrier_id': 1, + 'sales_tax_id': 23, + 'pricelist_id': user_pricelist or product_pricelist_default_discount_id, + 'payment_term_id': 26, + 'team_id': 2, + 'company_id': 1, + 'currency_id': 12, + 'source_id': 59, + 'state': 'draft', + 'picking_policy': 'direct', + 'partner_id': params['value']['partner_id'], + 'partner_shipping_id': params['value']['partner_shipping_id'], + 'real_shipping_id': params['value']['partner_shipping_id'], + 'partner_invoice_id': main_partner.id, + 'real_invoice_id': params['value']['partner_invoice_id'], + 'partner_purchase_order_name': params['value']['po_number'], + 'partner_purchase_order_file': params['value']['po_file'], + 'delivery_amt': params['value']['delivery_amount'] * 1.10, + 'estimated_arrival_days': params['value']['estimated_arrival_days'], + 'estimated_arrival_days_start': params['value']['estimated_arrival_days_start'], + 'shipping_cost_covered': 'customer', + 'shipping_paid_by': 'customer', + 'carrier_id': params['value']['carrier_id'], + 'delivery_service_type': params['value']['delivery_service_type'], + 'flash_sale': params['value']['flash_sale'], + 'note_website': params['value']['note_website'], + 'customer_type': sales_partner.customer_type if sales_partner else 'nonpkp', + 'npwp': sales_partner.npwp or '0', + 'sppkp': sales_partner.sppkp, + 'email': sales_partner.email, + 'user_id': 11314 + } + _logger.info(f"Order parameters: {parameters}") + + sales_partner = request.env['res.partner'].browse(parameters['partner_id']) + if sales_partner and sales_partner.user_id and sales_partner.user_id.id not in [25]: + parameters['user_id'] = sales_partner.user_id.id + _logger.info(f"Updated user_id from partner: {parameters['user_id']}") + + if params['value']['type'] == 'sale_order': + parameters['approval_status'] = 'pengajuan1' + _logger.info("Setting approval_status to 'pengajuan1'") + + sale_order = request.env['sale.order'].with_context(from_website_checkout=True).create([parameters]) + sale_order.onchange_partner_contact() + _logger.info(f"Created SO: {sale_order.id} - {sale_order.name}") + + user_id = params['value']['user_id'] + user_cart = request.env['website.user.cart'] + source = params['value']['source'] + _logger.info(f"Getting cart for user: {user_id}, source: {source}") + + carts = user_cart.get_product_by_user(user_id=user_id, selected=True, source=source) + _logger.info(f"Found {len(carts)} cart items") + + promotions = [] + for idx, cart in enumerate(carts, 1): + _logger.info(f"\n=== Processing Cart Item {idx}/{len(carts)} ===") + _logger.info(f"Full cart data: {cart}") + + if cart['cart_type'] == 'product': + product = request.env['product.product'].browse(cart['id']) + _logger.info(f"Product: {product.id} - {product.name}") + _logger.info(f"Cart Price Data: {cart['price']}") + + # Determine discount status based on: + # 1. has_flashsale flag from cart data + # 2. discount percentage > 0 + # 3. global flash sale parameter + is_flash_sale_item = cart.get('has_flashsale', False) + discount_percent = float(cart['price'].get('discount_percentage', 0)) + global_flash_sale = params['value'].get('flash_sale', False) + + is_has_disc = False + + # Item is considered discounted if: + # - It's specifically marked as flash sale item, OR + # - It has significant discount (>0%) and not affected by global flash sale + if is_flash_sale_item: + is_has_disc = True + _logger.info("Item is flash sale product - marked as discounted") + elif global_flash_sale: + _logger.info("Global flash sale active but item not eligible - not marked as discounted") + + _logger.info(f"Final is_has_disc: {is_has_disc}") + + order_line = request.env['sale.order.line'].create({ + 'company_id': 1, + 'order_id': sale_order.id, + 'product_id': product.id, + 'product_uom_qty': cart['quantity'], + 'product_available_quantity': cart['available_quantity'], + 'price_unit': cart['price']['price'], + 'discount': discount_percent, + 'is_has_disc': is_has_disc + }) + _logger.info(f"Created order line: {order_line.id}") + + order_line.product_id_change() + order_line.weight = order_line.product_id.weight + order_line.onchange_vendor_id() + _logger.info(f"After onchanges - Price: {order_line.price_unit}, Disc: {order_line.discount}") + + elif cart['cart_type'] == 'promotion': + promotions.append({ + 'order_id': sale_order.id, + 'program_line_id': cart['id'], + 'quantity': cart['quantity'] + }) + _logger.info(f"Added promotion: {cart['id']}") + + _logger.info("Processing promotions...") + sale_order._compute_etrts_date() + request.env['sale.order.promotion'].create(promotions) + + if len(promotions) > 0: + _logger.info(f"Applying {len(promotions)} promotions") + sale_order.apply_promotion_program() + sale_order.add_free_product(promotions) + + voucher_code = params['value']['voucher'] + if voucher_code: + _logger.info(f"Processing voucher: {voucher_code}") + voucher = request.env['voucher'].search( + [('code', '=', voucher_code), ('apply_type', 'in', ['all', 'brand'])], limit=1) + voucher_shipping = request.env['voucher'].search( + [('code', '=', voucher_code), ('apply_type', 'in', ['shipping'])], limit=1) + + if voucher and len(promotions) == 0: + _logger.info("Applying regular voucher") + sale_order.voucher_id = voucher.id + sale_order.apply_voucher() + + if voucher_shipping and len(promotions) == 0: + _logger.info("Applying shipping voucher") + sale_order.voucher_shipping_id = voucher_shipping.id + sale_order.apply_voucher_shipping() + + cart_ids = [x['cart_id'] for x in carts] + if sale_order._requires_approval_margin_leader(): + sale_order.approval_status = 'pengajuan2' + _logger.info("Approval status set to 'pengajuan2'") + elif sale_order._requires_approval_margin_manager(): + sale_order.approval_status = 'pengajuan1' + _logger.info("Approval status set to 'pengajuan1'") + + sale_order._auto_set_shipping_from_website() + _logger.info("=== END CREATE PARTNER SALE ORDER ===") + return self.response({ + 'id': sale_order.id, + 'name': sale_order.name + }) + + except Exception as e: + _logger.error(f"Error in create_partner_sale_order: {str(e)}", exc_info=True) + return self.response(code=500, description=str(e)) @http.route(PREFIX_PARTNER + 'sale-order/<id>/awb', auth='public', methods=['GET', 'OPTIONS']) @controller.Controller.must_authorized(private=True, private_key='partner_id') diff --git a/indoteknik_api/controllers/api_v1/stock_picking.py b/indoteknik_api/controllers/api_v1/stock_picking.py index 85b0fbba..762e17c5 100644 --- a/indoteknik_api/controllers/api_v1/stock_picking.py +++ b/indoteknik_api/controllers/api_v1/stock_picking.py @@ -125,28 +125,33 @@ class StockPicking(controller.Controller): @http.route(prefix + 'stock-picking/<scanid>/documentation', auth='public', methods=['PUT', 'OPTIONS'], csrf=False) @controller.Controller.must_authorized() def write_partner_stock_picking_documentation(self, **kw): - scanid = int(kw.get('scanid', 0)) + scanid = kw.get('scanid', '').strip() sj_document = kw.get('sj_document', False) paket_document = kw.get('paket_document', False) - params = {'sj_documentation': sj_document, - 'paket_documentation': paket_document, - 'driver_arrival_date': datetime.utcnow(), - } + params = { + 'sj_documentation': sj_document, + 'paket_documentation': paket_document, + 'driver_arrival_date': datetime.utcnow(), + } - picking_data = request.env['stock.picking'].search([('id', '=', scanid)], limit=1) + picking_data = False + if scanid.isdigit() and int(scanid) < 2147483647: + picking_data = request.env['stock.picking'].search([('id', '=', int(scanid))], limit=1) if not picking_data: picking_data = request.env['stock.picking'].search([('picking_code', '=', scanid)], limit=1) if not picking_data: return self.response(code=404, description='picking not found') + picking_data.write(params) return self.response({ 'name': picking_data.name }) + @http.route(prefix + 'webhook/biteship', type='json', auth='public', methods=['POST'], csrf=False) def update_status_from_biteship(self, **kw): _logger.info("Biteship Webhook: Request received at controller start (type='json').") diff --git a/indoteknik_api/controllers/api_v1/voucher.py b/indoteknik_api/controllers/api_v1/voucher.py index 9ffeeace..0338360b 100644 --- a/indoteknik_api/controllers/api_v1/voucher.py +++ b/indoteknik_api/controllers/api_v1/voucher.py @@ -74,7 +74,7 @@ class Voucher(controller.Controller): partner_voucher_orders = [] for order in voucher.order_ids: - if order.partner_id.id == user.partner_id.id: + if order.partner_id.id == user.partner_id.id and order.state != 'cancel' and (order.payment_status or order.payment_status is None): partner_voucher_orders.append(order) if voucher.limit_user > 0 and len(partner_voucher_orders) >= voucher.limit_user: |
