diff options
39 files changed, 528 insertions, 100 deletions
diff --git a/indoteknik_api/controllers/api_v1/banner.py b/indoteknik_api/controllers/api_v1/banner.py index d1ebf573..308d2765 100644 --- a/indoteknik_api/controllers/api_v1/banner.py +++ b/indoteknik_api/controllers/api_v1/banner.py @@ -40,6 +40,8 @@ class Banner(controller.Controller): 'sequence': banner.sequence, 'group_by_week': banner.group_by_week, 'image': request.env['ir.attachment'].api_image('x_banner.banner', 'x_banner_image', banner.id), + 'headline_banner': banner.x_headline_banner, + 'description_banner': banner.x_description_banner } if banner.group_by_week and int(banner.group_by_week) < week_number and type == 'index-a-1': diff --git a/indoteknik_api/controllers/api_v1/flash_sale.py b/indoteknik_api/controllers/api_v1/flash_sale.py index dff8bec3..00b1f2e0 100644 --- a/indoteknik_api/controllers/api_v1/flash_sale.py +++ b/indoteknik_api/controllers/api_v1/flash_sale.py @@ -14,7 +14,7 @@ class FlashSale(controller.Controller): def _get_flash_sale_header(self, **kw): try: # base_url = request.env['ir.config_parameter'].get_param('web.base.url') - active_flash_sale = request.env['product.pricelist'].get_active_flash_sale() + active_flash_sale = request.env['product.pricelist'].get_is_show_program_flash_sale() data = [] for pricelist in active_flash_sale: query = [ @@ -24,6 +24,7 @@ class FlashSale(controller.Controller): 'pricelist_id': pricelist.id, 'option': pricelist.flashsale_option, 'name': pricelist.name, + 'is_show_program': pricelist.is_show_program, 'banner': request.env['ir.attachment'].api_image('product.pricelist', 'banner', pricelist.id), 'banner_mobile': request.env['ir.attachment'].api_image('product.pricelist', 'banner_mobile', pricelist.id), 'banner_top': request.env['ir.attachment'].api_image('product.pricelist', 'banner_top', pricelist.id), diff --git a/indoteknik_api/controllers/api_v1/sale_order.py b/indoteknik_api/controllers/api_v1/sale_order.py index ee295b55..b35da7a2 100644 --- a/indoteknik_api/controllers/api_v1/sale_order.py +++ b/indoteknik_api/controllers/api_v1/sale_order.py @@ -9,6 +9,36 @@ class SaleOrder(controller.Controller): prefix = '/api/v1/' PREFIX_PARTNER = prefix + 'partner/<partner_id>/' + @http.route(prefix + "sale_order/<id>/reject/<product_id>", auth='public', method=['POST', 'OPTIONS'], csrf=False) + @controller.Controller.must_authorized() + def reject_sale_order_line(self, **kw): + so_id = int(kw.get('id', '0')) + product_id = int(kw.get('product_id', '0')) + params = self.get_request_params(kw, { + 'reason_reject': [] + }) + + sale_order_line = request.env['sale.order.line'].search([ + ('product_id', '=', product_id), + ('order_id', '=', so_id) + ], limit=1) + + if sale_order_line: + parameters = { + 'sale_order_id': sale_order_line.order_id.id, + 'product_id': sale_order_line.product_id.id, + 'qty_reject': sale_order_line.product_uom_qty, + 'reason_reject': params['value']['reason_reject'], + } + + sale_order_reject = request.env['sales.order.reject'].create(parameters) + + sale_order_line.unlink() + + return self.response('work') + else: + return self.response('Sale order line not found', status=404) + @http.route(prefix + "sale_order_number", auth='public', method=['GET', 'OPTIONS']) @controller.Controller.must_authorized() def get_number_sale_order(self, **kw): @@ -97,7 +127,7 @@ class SaleOrder(controller.Controller): return self.response(data) @http.route(PREFIX_PARTNER + 'sale_order/<id>', auth='public', method=['GET', 'OPTIONS']) - @controller.Controller.must_authorized(private=True, private_key='partner_id') + @controller.Controller.must_authorized() def partner_get_sale_order_detail(self, **kw): params = self.get_request_params(kw, { 'partner_id': ['number'], @@ -313,7 +343,7 @@ class SaleOrder(controller.Controller): return self.response(result) @http.route(PREFIX_PARTNER + 'sale_order/checkout', auth='public', method=['POST', 'OPTIONS'], csrf=False) - @controller.Controller.must_authorized(private=True, private_key='partner_id') + @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')) @@ -332,6 +362,7 @@ class SaleOrder(controller.Controller): 'carrier_id': [], 'delivery_service_type': [], 'flash_sale': ['boolean'], + 'note_website': [], 'voucher': [], 'source': [], 'estimated_arrival_days': ['number', 'default:0'] @@ -366,9 +397,10 @@ class SaleOrder(controller.Controller): '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': 'nonpkp', 'npwp': '0', - 'user_id': 20 # User ID: Nabila Rahmawati + 'user_id': 3222 # User ID: Nadia Rauhadatul Firdaus } if params['value']['type'] == 'sale_order': parameters['approval_status'] = 'pengajuan1' diff --git a/indoteknik_api/models/product_pricelist.py b/indoteknik_api/models/product_pricelist.py index 0d4247c8..6e88517c 100644 --- a/indoteknik_api/models/product_pricelist.py +++ b/indoteknik_api/models/product_pricelist.py @@ -95,6 +95,20 @@ class ProductPricelist(models.Model): ], limit=1, order='start_date asc') return pricelist + def get_is_show_program_flash_sale(self): + """ + Check whether have active flash sale in range of date + @return: returns pricelist: object + """ + current_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S') + pricelist = self.search([ + ('is_flash_sale', '=', True), + ('is_show_program', '=', True), + ('start_date', '<=', current_time), + ('end_date', '>=', current_time) + ], order='start_date asc') + return pricelist + def is_flash_sale_product(self, product_id: int): """ Check whether product is flash sale. diff --git a/indoteknik_api/models/sale_order.py b/indoteknik_api/models/sale_order.py index 88dc331f..725dbb4b 100644 --- a/indoteknik_api/models/sale_order.py +++ b/indoteknik_api/models/sale_order.py @@ -65,6 +65,7 @@ class SaleOrder(models.Model): data_with_detail = { 'payment_term': sale_order.payment_term_id.name or '', 'products': [], + 'products_reject_line': [], 'delivery_amount': sale_order.delivery_amt or 0, 'address': { 'site_partner': sale_order.partner_id.site_id.name, @@ -87,6 +88,11 @@ class SaleOrder(models.Model): for invoice in sale_order.invoice_ids: if invoice.state == 'posted': data_with_detail['invoices'].append(self.env['account.move'].api_v1_single_response(invoice)) + for reject in sale_order.reject_line: + if len(reject) > 0: + product_reject = self.env['product.product'].api_single_response(reject.product_id) + product_reject['quantity'] = reject.qty_reject + data_with_detail['products_reject_line'].append(product_reject) data.update(data_with_detail) else: data_with_detail = { diff --git a/indoteknik_custom/__manifest__.py b/indoteknik_custom/__manifest__.py index 724796c0..6848ca7c 100755 --- a/indoteknik_custom/__manifest__.py +++ b/indoteknik_custom/__manifest__.py @@ -93,6 +93,7 @@ 'views/product_attribute_value.xml', 'views/mail_template_po.xml', 'views/mail_template_efaktur.xml', + 'views/mail_template_invoice_po.xml', 'views/price_group.xml', 'views/mrp_production.xml', 'views/apache_solr.xml', diff --git a/indoteknik_custom/models/__init__.py b/indoteknik_custom/models/__init__.py index a6bab518..ee9c9429 100755 --- a/indoteknik_custom/models/__init__.py +++ b/indoteknik_custom/models/__init__.py @@ -122,3 +122,4 @@ from . import logbook_bill from . import report_logbook_bill from . import sale_order_multi_uangmuka_penjualan from . import shipment_group +from . import sales_order_reject diff --git a/indoteknik_custom/models/commision.py b/indoteknik_custom/models/commision.py index 7ec2cecc..c5809005 100644 --- a/indoteknik_custom/models/commision.py +++ b/indoteknik_custom/models/commision.py @@ -316,6 +316,7 @@ class CustomerCommisionLine(models.Model): dpp = fields.Float(string='DPP') tax = fields.Float(string='TaxAmt') total = fields.Float(string='Total') + total_percent_margin = fields.Float('Total Margin', related='invoice_id.sale_id.total_percent_margin') product_id = fields.Many2one('product.product', string='Product') class AccountMove(models.Model): diff --git a/indoteknik_custom/models/product_pricelist.py b/indoteknik_custom/models/product_pricelist.py index b7a6d77e..2b17cf6e 100644 --- a/indoteknik_custom/models/product_pricelist.py +++ b/indoteknik_custom/models/product_pricelist.py @@ -6,6 +6,7 @@ class ProductPricelist(models.Model): _inherit = 'product.pricelist' is_flash_sale = fields.Boolean(string='Flash Sale', default=False) + is_show_program = fields.Boolean(string='Show Program', default=False) banner = fields.Binary(string='Banner') start_date = fields.Datetime(string='Start Date') end_date = fields.Datetime(string='End Date') diff --git a/indoteknik_custom/models/promotion/promotion_program_line.py b/indoteknik_custom/models/promotion/promotion_program_line.py index d253c68d..a57f1f2c 100644 --- a/indoteknik_custom/models/promotion/promotion_program_line.py +++ b/indoteknik_custom/models/promotion/promotion_program_line.py @@ -23,7 +23,7 @@ class PromotionProgramLine(models.Model): display_on_homepage = fields.Boolean('Display on Homepage') price = fields.Float('Price') - sequence = fields.Integer(string='Sequence', default=0) + sequence = fields.Integer(string='Sequence') discount_type = fields.Selection([ ("percentage", "Percentage"), ("fixed", "Fixed") @@ -106,9 +106,12 @@ class PromotionProgramLine(models.Model): products_total = sum(x['price']['price'] * x['qty'] / qty for x in products) free_products_total = sum(x['price']['price'] * x['qty'] / qty for x in free_products) package_price = products_total + free_products_total + + image = self.env['ir.attachment'].api_image('promotion.program', 'image', self.program_id.id), response = { 'id': self.id, + 'image_program': image, 'name': self.name, 'remaining_time': self._get_remaining_time(), 'promotion_type': self._res_promotion_type(), diff --git a/indoteknik_custom/models/purchase_order.py b/indoteknik_custom/models/purchase_order.py index 32ddf1e5..c6512772 100755 --- a/indoteknik_custom/models/purchase_order.py +++ b/indoteknik_custom/models/purchase_order.py @@ -13,7 +13,7 @@ except ImportError: _logger = logging.getLogger(__name__) - + class PurchaseOrder(models.Model): _inherit = 'purchase.order' @@ -65,7 +65,15 @@ class PurchaseOrder(models.Model): ('not_printed', 'Belum Print'), ('printed', 'Printed') ], string='Printed?', copy=False, tracking=True) + date_done_picking = fields.Datetime(string='Date Done Picking', compute='get_date_done') + def get_date_done(self): + picking = self.env['stock.picking'].search([ + ('purchase_id', '=', self.id), + ('state', '=', 'done') + ], limit=1, order='create_date desc') + + self.date_done_picking = picking.date_done def _prepare_invoice(self): """Prepare the dict of values to create the new invoice for a purchase order. """ @@ -75,12 +83,7 @@ class PurchaseOrder(models.Model): if not journal: raise UserError(_('Please define an accounting purchase journal for the company %s (%s).') % (self.company_id.name, self.company_id.id)) - stock_picking = self.env['stock.picking'].search([ - ('purchase_id', '=', self.id), - ('state', '=', 'done') - ], order='date_done desc', limit=1) - - date_done = stock_picking.date_done + date_done = self.date_approve day_extension = int(self.payment_term_id.line_ids.days) payment_schedule = date_done + timedelta(days=day_extension) @@ -146,7 +149,7 @@ class PurchaseOrder(models.Model): 'location_dest_id': self._get_destination_location(), 'location_id': self.partner_id.property_stock_supplier.id, 'company_id': self.company_id.id, - 'sale_order': sale_order, + 'sale_order': sale_order } @api.model @@ -557,6 +560,9 @@ class PurchaseOrder(models.Model): self.approval_status = 'pengajuan1' def re_calculate(self): + if self.from_apo: + self.re_calculate_from_apo() + return for line in self.order_line: sale_order_line = self.env['sale.order.line'].search([ ('product_id', 'in', [line.product_id.id]), @@ -565,6 +571,14 @@ class PurchaseOrder(models.Model): for so_line in sale_order_line: so_line.purchase_price = line.price_unit + def re_calculate_from_apo(self): + for line in self.order_sales_match_line: + order_line = self.env['purchase.order.line'].search([ + ('product_id', '=', line.product_id.id), + ('order_id', '=', line.purchase_order_id.id) + ], limit=1) + line.sale_line_id.purchase_price = order_line.price_unit + def button_cancel(self): res = super(PurchaseOrder, self).button_cancel() self.approval_status = False @@ -623,25 +637,29 @@ class PurchaseOrder(models.Model): ('order_id', '=', line.sale_id.id) ], limit=1, order='price_reduce_taxexcl') - sum_so_margin += line.qty_po / line.qty_so * sale_order_line.item_margin - # sales_price = sale_order_line.price_reduce_taxexcl * sale_order_line.product_uom_qty - sales_price = sale_order_line.price_reduce_taxexcl * po_line.product_qty - if sale_order_line.order_id.shipping_cost_covered == 'indoteknik': - sales_price -= sale_order_line.delivery_amt_line - if sale_order_line.order_id.fee_third_party > 0: - sales_price -= sale_order_line.fee_third_party_line - sum_sales_price += sales_price - purchase_price = po_line.price_subtotal - if line.purchase_order_id.delivery_amount > 0: - purchase_price += po_line.delivery_amt_line - real_item_margin = sales_price - purchase_price - sum_margin += real_item_margin + if sale_order_line and po_line: + so_margin = (line.qty_po / line.qty_so) * sale_order_line.item_margin + sum_so_margin += so_margin + + sales_price = sale_order_line.price_reduce_taxexcl * line.qty_po + if sale_order_line.order_id.shipping_cost_covered == 'indoteknik': + sales_price -= (sale_order_line.delivery_amt_line / sale_order_line.product_uom_qty) * line.qty_po + if sale_order_line.order_id.fee_third_party > 0: + sales_price -= (sale_order_line.fee_third_party_line / sale_order_line.product_uom_qty) * line.qty_po + sum_sales_price += sales_price + + purchase_price = po_line.price_subtotal / po_line.product_qty * line.qty_po + if line.purchase_order_id.delivery_amount > 0: + purchase_price += (po_line.delivery_amt_line / po_line.product_qty) * line.qty_po + real_item_margin = sales_price - purchase_price + sum_margin += real_item_margin if sum_so_margin != 0 and sum_sales_price != 0 and sum_margin != 0: self.total_so_margin = sum_so_margin self.total_so_percent_margin = round((sum_so_margin / sum_sales_price), 2) * 100 self.total_margin = sum_margin self.total_percent_margin = round((sum_margin / sum_sales_price), 2) * 100 + else: self.total_margin = 0 self.total_percent_margin = 0 diff --git a/indoteknik_custom/models/purchase_order_line.py b/indoteknik_custom/models/purchase_order_line.py index 8a3b3930..7af84b48 100755 --- a/indoteknik_custom/models/purchase_order_line.py +++ b/indoteknik_custom/models/purchase_order_line.py @@ -82,7 +82,7 @@ class PurchaseOrderLine(models.Model): 'sale_id': sale_id, } - @api.constrains('price_unit') + # @api.constrains('price_unit') def constrains_purchase_price(self): for line in self: matches_so = self.env['purchase.order.sales.match'].search([ diff --git a/indoteknik_custom/models/report_stock_forecasted.py b/indoteknik_custom/models/report_stock_forecasted.py index d5e48fdc..5f9427f8 100644 --- a/indoteknik_custom/models/report_stock_forecasted.py +++ b/indoteknik_custom/models/report_stock_forecasted.py @@ -13,6 +13,8 @@ class ReplenishmentReport(models.AbstractModel): if document_out and "SO/" in document_out.name: order_id = document_out.id + if document_out == False: + continue product_id = line.get('product', {}).get('id') query = [('product_id', '=', product_id)] @@ -32,7 +34,7 @@ class ReplenishmentReport(models.AbstractModel): 'qty_fullfillment': quantity, }) - return lines + return lines def _calculate_result(self, line): if line['document_in']: diff --git a/indoteknik_custom/models/res_partner.py b/indoteknik_custom/models/res_partner.py index ea06854d..19699aab 100644 --- a/indoteknik_custom/models/res_partner.py +++ b/indoteknik_custom/models/res_partner.py @@ -40,6 +40,8 @@ class ResPartner(models.Model): ('PNR', 'Pareto Non Repeating'), ('NP', 'Non Pareto') ]) + email_finance = fields.Char(string='Email Finance') + email_sales = fields.Char(string='Email Sales') user_payment_terms_sales = fields.Many2one('res.users', string='Users Update Payment Terms') date_payment_terms_sales = fields.Datetime(string='Date Update Payment Terms') diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index 0d28e677..9093b4c4 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -11,6 +11,7 @@ class SaleOrder(models.Model): _inherit = "sale.order" fullfillment_line = fields.One2many('sales.order.fullfillment', 'sales_order_id', string='Fullfillment') + reject_line = fields.One2many('sales.order.reject', 'sale_order_id', string='Reject Lines') order_sales_match_line = fields.One2many('sales.order.purchase.match', 'sales_order_id', string='Purchase Match Lines', states={'cancel': [('readonly', True)], 'done': [('readonly', True)]}, copy=True) total_margin = fields.Float('Total Margin', compute='_compute_total_margin', help="Total Margin in Sales Order Header") total_percent_margin = fields.Float('Total Percent Margin', compute='_compute_total_percent_margin', help="Total % Margin in Sales Order Header") @@ -67,7 +68,7 @@ class SaleOrder(models.Model): ('partial_chargeback', 'Partial Chargeback'), ('authorize', 'Authorize'), ], tracking=True, string='Payment Status', help='Payment Gateway Status / Midtrans / Web, https://docs.midtrans.com/en/after-payment/status-cycle') - date_doc_kirim = fields.Datetime(string='Tanggal Kirim di SJ', help="Tanggal Kirim di cetakan SJ yang terakhir, tidak berpengaruh ke Accounting", tracking=True) + date_doc_kirim = fields.Datetime(string='Tanggal Kirim di SJ', help="Tanggal Kirim di cetakan SJ yang terakhir, tidak berpengaruh ke Accounting") payment_type = fields.Char(string='Payment Type', help='Jenis pembayaran dengan Midtrans') gross_amount = fields.Float(string='Gross Amount', help='Jumlah pembayaran yang dilakukan dengan Midtrans') notification = fields.Char(string='Notification', help='Dapat membantu error dari approval') @@ -79,8 +80,8 @@ class SaleOrder(models.Model): ('pkp', 'PKP'), ('nonpkp', 'Non PKP') ], required=True) - sppkp = fields.Char(string="SPPKP", required=True) - npwp = fields.Char(string="NPWP", required=True) + sppkp = fields.Char(string="SPPKP", required=True, tracking=True) + npwp = fields.Char(string="NPWP", required=True, tracking=True) purchase_total = fields.Monetary(string='Purchase Total', compute='_compute_purchase_total') voucher_id = fields.Many2one(comodel_name='voucher', string='Voucher', copy=False) applied_voucher_id = fields.Many2one(comodel_name='voucher', string='Applied Voucher', copy=False) @@ -100,6 +101,19 @@ class SaleOrder(models.Model): ], string='Web Approval', copy=False) compute_fullfillment = fields.Boolean(string='Compute Fullfillment', compute="_compute_fullfillment") note_ekspedisi = fields.Char(string="Note Ekspedisi") + date_kirim_ril = fields.Datetime(string='Tanggal Kirim SJ', compute='_compute_date_kirim', copy=False) + date_status_done = fields.Datetime(string='Date Done DO', compute='_compute_date_kirim', copy=False) + date_driver_arrival = fields.Datetime(string='Arrival Date', compute='_compute_date_kirim', copy=False) + date_driver_departure = fields.Datetime(string='Departure Date', compute='_compute_date_kirim', copy=False) + note_website = fields.Char(string="Note Website") + + def _compute_date_kirim(self): + for rec in self: + picking = self.env['stock.picking'].search([('sale_id', '=', rec.id), ('state', 'not in', ['cancel'])], order='date_doc_kirim desc', limit=1) + rec.date_kirim_ril = picking.date_doc_kirim + rec.date_status_done = picking.date_done + rec.date_driver_arrival = picking.driver_arrival_date + rec.date_driver_departure = picking.driver_departure_date def open_form_multi_create_uang_muka(self): action = self.env['ir.actions.act_window']._for_xml_id('indoteknik_custom.action_sale_order_multi_uangmuka') @@ -468,19 +482,7 @@ class SaleOrder(models.Model): if order.payment_term_id != partner.property_payment_term_id: raise UserError("Payment Term berbeda pada Master Data Customer") if not order.client_order_ref and order.create_date > datetime(2024, 6, 27): - raise UserError("Customer Reference kosong, di isi dengan NO PO jika PO tidak ada mohon ditulis Tanpa PO") - - if order.validate_partner_invoice_due(): - return self._create_notification_action('Notification', 'Terdapat invoice yang telah melewati batas waktu, mohon perbarui pada dokumen Due Extension') - - if order._requires_approval_margin_leader(): - order.approval_status = 'pengajuan2' - return self._create_approval_notification('Pimpinan') - elif order._requires_approval_margin_manager(): - order.approval_status = 'pengajuan1' - return self._create_approval_notification('Sales Manager') - - raise UserError("Bisa langsung Confirm") + raise UserError("Customer Reference kosong, di isi dengan NO PO jika PO tidak ada mohon ditulis Tanpa PO") def send_notif_to_salesperson(self, cancel=False): @@ -554,6 +556,7 @@ class SaleOrder(models.Model): def action_confirm(self): for order in self: + order.sale_order_approve() order._validate_order() order.order_line.validate_line() diff --git a/indoteknik_custom/models/sale_order_line.py b/indoteknik_custom/models/sale_order_line.py index 1fee041d..1f90b821 100644 --- a/indoteknik_custom/models/sale_order_line.py +++ b/indoteknik_custom/models/sale_order_line.py @@ -153,18 +153,21 @@ class SaleOrderLine(models.Model): query = [('product_id', '=', line.product_id.id), ('vendor_id', '=', line.product_id.x_manufacture.override_vendor_id.id)] purchase_price = self.env['purchase.pricelist'].search( - query, limit=1, order='count_trx_po desc, count_trx_po_vendor desc') + query, limit=1, order='count_trx_po desc, count_trx_po_vendor desc') line.vendor_id = purchase_price.vendor_id line.tax_id = line.order_id.sales_tax_id price, taxes = line._get_valid_purchase_price(purchase_price) line.purchase_price = price - line_name = ('[' + line.product_id.default_code + ']' if line.product_id.default_code else '') + ' ' + (line.product_id.name if line.product_id.name else '') + ' ' + \ - ('(' + line.product_id.product_template_attribute_value_ids.name + ')' if line.product_id.product_template_attribute_value_ids.name else '') + ' ' + \ - (line.product_id.short_spesification if line.product_id.short_spesification else '') + attribute_values = line.product_id.product_template_attribute_value_ids.mapped('name') + attribute_values_str = ', '.join(attribute_values) if attribute_values else '' + + line_name = ('[' + line.product_id.default_code + ']' if line.product_id.default_code else '') + ' ' + \ + (line.product_id.name if line.product_id.name else '') + ' ' + \ + ('(' + attribute_values_str + ')' if attribute_values_str else '') + ' ' + \ + (line.product_id.short_spesification if line.product_id.short_spesification else '') line.name = line_name - def compute_delivery_amt_line(self): for line in self: try: diff --git a/indoteknik_custom/models/sales_order_reject.py b/indoteknik_custom/models/sales_order_reject.py new file mode 100644 index 00000000..9983c64e --- /dev/null +++ b/indoteknik_custom/models/sales_order_reject.py @@ -0,0 +1,15 @@ +from odoo import fields, models, api, _ +from odoo.exceptions import AccessError, UserError, ValidationError +from odoo.tools import DEFAULT_SERVER_DATETIME_FORMAT +import logging + +_logger = logging.getLogger(__name__) + + +class SalesOrderReject(models.Model): + _name = 'sales.order.reject' + + sale_order_id = fields.Many2one('sale.order', string='Sale Order') + product_id = fields.Many2one('product.product', string='Product') + qty_reject = fields.Float(string='Qty') + reason_reject = fields.Char(string='Reason Reject') diff --git a/indoteknik_custom/models/shipment_group.py b/indoteknik_custom/models/shipment_group.py index 92f76db7..df3f1bb4 100644 --- a/indoteknik_custom/models/shipment_group.py +++ b/indoteknik_custom/models/shipment_group.py @@ -31,6 +31,10 @@ class ShipmentGroupLine(models.Model): picking_id = fields.Many2one('stock.picking', string='Picking') sale_id = fields.Many2one('sale.order', string='Sale Order') state = fields.Char(string='Status', readonly=True, compute='_compute_state') + shipping_paid_by = fields.Selection([ + ('indoteknik', 'Indoteknik'), + ('customer', 'Customer') + ], string='Shipping Paid by', copy=False) @api.depends('picking_id.state') def _compute_state(self): @@ -60,6 +64,7 @@ class ShipmentGroupLine(models.Model): raise UserError('Partner must be same as shipment group') self.partner_id = picking.partner_id + self.shipping_paid_by = picking.sale_id.shipping_paid_by if not self.shipment_id.partner_id: self.shipment_id.partner_id = picking.partner_id diff --git a/indoteknik_custom/models/solr/product_template.py b/indoteknik_custom/models/solr/product_template.py index b3787499..3d7d3a80 100644 --- a/indoteknik_custom/models/solr/product_template.py +++ b/indoteknik_custom/models/solr/product_template.py @@ -108,7 +108,6 @@ class ProductTemplate(models.Model): "max_discount" : voucher.max_discount_amount } }) - print(document) self.solr().add(docs=[document], softCommit=True) products = self.env['product.product'].search([ diff --git a/indoteknik_custom/models/solr/promotion_program.py b/indoteknik_custom/models/solr/promotion_program.py index 0d417b3e..014e8062 100644 --- a/indoteknik_custom/models/solr/promotion_program.py +++ b/indoteknik_custom/models/solr/promotion_program.py @@ -30,6 +30,7 @@ class PromotionProgram(models.Model): 'id': rec.id, 'name_s': rec.name, 'banner_s': ir_attachment.api_image(self._name, 'banner', rec.id) if rec.banner else '', + 'image_s': ir_attachment.api_image(self._name, 'image', rec.id) if rec.image else '', 'keywords': [x.name for x in rec.keyword_ids], 'line_ids': [x.id for x in rec.program_line], 'start_time_s': self._time_format(rec.start_time), @@ -66,3 +67,17 @@ class PromotionProgram(models.Model): for line in rec.program_line: line._create_solr_queue('_sync_to_solr') + def solr_flag_to_queue(self, limit=500): + domain = [ + ('solr_flag', '=', 2), + ('active', 'in', [True, False]) + ] + records = self.search(domain, limit=limit) + for record in records: + record._create_solr_queue('_sync_to_solr') + record.solr_flag = 1 + + def action_sync_to_solr(self): + rec_ids = self.env.context.get('active_ids', []) + recs = self.search([('id', 'in', rec_ids)]) + recs._create_solr_queue('_sync_to_solr')
\ No newline at end of file diff --git a/indoteknik_custom/models/solr/promotion_program_line.py b/indoteknik_custom/models/solr/promotion_program_line.py index 73504c48..4b0e67f6 100644 --- a/indoteknik_custom/models/solr/promotion_program_line.py +++ b/indoteknik_custom/models/solr/promotion_program_line.py @@ -37,6 +37,9 @@ class PromotionProgramLine(models.Model): promotion_type = rec._res_promotion_type() + # Set sequence_i to None if rec.sequence is 0 + sequence_value = None if rec.sequence == 0 else rec.sequence + document.update({ 'id': rec.id, 'program_id_i': rec.program_id.id or 0, @@ -47,7 +50,7 @@ class PromotionProgramLine(models.Model): 'package_limit_user_i': rec.package_limit_user, 'package_limit_trx_i': rec.package_limit_trx, 'price_f': rec.price, - 'sequence_i': rec.sequence, + 'sequence_i': sequence_value, 'product_ids': [x.product_id.id for x in rec.product_ids], 'products_s': json.dumps(products), 'free_product_ids': [x.product_id.id for x in rec.free_product_ids], diff --git a/indoteknik_custom/models/solr/x_banner_banner.py b/indoteknik_custom/models/solr/x_banner_banner.py index 67739d47..8452644c 100644 --- a/indoteknik_custom/models/solr/x_banner_banner.py +++ b/indoteknik_custom/models/solr/x_banner_banner.py @@ -23,7 +23,7 @@ class XBannerBanner(models.Model): 'function_name': function_name }) - @api.constrains('x_name', 'x_url_banner', 'background_color', 'x_banner_image', 'x_banner_category', 'x_relasi_manufacture', 'x_sequence_banner', 'x_status_banner', 'sequence', 'group_by_week') + @api.constrains('x_name', 'x_url_banner', 'background_color', 'x_banner_image', 'x_banner_category', 'x_relasi_manufacture', 'x_sequence_banner', 'x_status_banner', 'sequence', 'group_by_week', 'headline_banner_s', 'description_banner_s') def _create_solr_queue_sync_brands(self): self._create_solr_queue('_sync_banners_to_solr') @@ -49,6 +49,8 @@ class XBannerBanner(models.Model): 'category_id_i': banners.x_banner_category.id or '', 'manufacture_id_i': banners.x_relasi_manufacture.id or '', 'group_by_week': banners.group_by_week or '', + 'headline_banner_s': banners.x_headline_banner or '', + 'description_banner_s': banners.x_description_banner or '', }) self.solr().add([document]) banners.update_last_update_solr() diff --git a/indoteknik_custom/models/stock_move.py b/indoteknik_custom/models/stock_move.py index 9c991be3..fe46bf65 100644 --- a/indoteknik_custom/models/stock_move.py +++ b/indoteknik_custom/models/stock_move.py @@ -73,4 +73,5 @@ class StockMoveLine(models.Model): _inherit = 'stock.move.line' line_no = fields.Integer('No', default=0) + note = fields.Char('Note') manufacture = fields.Many2one('x_manufactures', string="Brands", related="product_id.x_manufacture", store=True) diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index daa05c14..c151a543 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -71,7 +71,10 @@ class StockPicking(models.Model): ('not_paid', 'Customer belum bayar'), ('partial', 'Kirim Parsial'), ('not_complete', 'Belum Lengkap'), - ('indent', 'Indent') + ('indent', 'Indent'), + ('self_pickup', 'Barang belum di pickup Customer'), + ('delivery_route', 'Belum masuk rute pengiriman'), + ('expedition_closed', 'Eskpedisi belum buka') ], string='Note Logistic', help='jika field ini diisi maka tidak akan dihitung ke lead time') 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") @@ -87,6 +90,14 @@ class StockPicking(models.Model): date_unreserve = fields.Datetime(string="Date Unreserved", copy=False, tracking=True) date_availability = fields.Datetime(string="Date Availability", copy=False, tracking=True) sale_order = fields.Char(string='Matches SO', copy=False) + printed_sj = fields.Boolean('Printed Surat Jalan', help='flag which is internal use or not') + + def reset_status_printed(self): + for rec in self: + rec.status_printed = 'not_printed' + rec.printed_sj = False + rec.date_printed_list = False + rec.date_printed_sj = False @api.onchange('carrier_id') def constrains_carrier_id(self): diff --git a/indoteknik_custom/models/website_user_cart.py b/indoteknik_custom/models/website_user_cart.py index bbff6035..793dda0b 100644 --- a/indoteknik_custom/models/website_user_cart.py +++ b/indoteknik_custom/models/website_user_cart.py @@ -1,5 +1,5 @@ from odoo import fields, models - +from datetime import datetime, timedelta class WebsiteUserCart(models.Model): _name = 'website.user.cart' @@ -15,6 +15,7 @@ class WebsiteUserCart(models.Model): ('buy', 'Buy') ], 'Source', default='add_to_cart') user_other_carts = fields.One2many('website.user.cart', 'id', 'Other Products', compute='_compute_user_other_carts') + is_reminder = fields.Boolean(string='Reminder?') def _compute_user_other_carts(self): for record in self: @@ -129,10 +130,83 @@ class WebsiteUserCart(models.Model): } return result - def action_mail_reminder_to_checkout(self, id): - template = self.env.ref('indoteknik_custom.mail_template_user_cart_reminder_to_checkout') - template.send_mail(int(id), force_send=True) + def action_mail_reminder_to_checkout(self): + # user_ids = self.search([]).mapped('user_id') + + user_ids = [101] + for user in user_ids: + latest_cart = self.search([('user_id', '=', user), ('is_reminder', '=', False)], order='create_date desc', limit=1) + + carts_to_remind = self.search([('user_id', '=', user)]) + if latest_cart and not latest_cart.is_reminder: + for cart in carts_to_remind: + if not cart.program_line_id: + cart.is_selected = True + cart.is_reminder = True + template = self.env.ref('indoteknik_custom.mail_template_user_cart_reminder_to_checkout') + template.send_mail(latest_cart.id, force_send=True) + + def calculate_discount(self, user_id): + carts = self.search([('user_id', '=', user_id)]) + voucher = self.env['voucher'].browse(146) + total_discount = 0.01 + total_voucher = 0.0 + subtotal_website = 0.0 + + for cart in carts: + product_manufacture = cart.product_id.x_manufacture.id + product_price = cart.get_price_website(cart.product_id.id) + subtotal = product_price['price'] * cart.qty + subtotal_website += product_price['price'] + discount_amount = 0.0 + for line in voucher.voucher_line: + if line.manufacture_id.id == product_manufacture: + discount_amount = line.discount_amount + break + + if discount_amount > 0 and subtotal > 0: + product_discount = subtotal - (subtotal * (discount_amount / 100.0)) + voucher_product = subtotal * (discount_amount / 100.0) + total_discount += product_discount + total_voucher += voucher_product + + if total_discount > 0: + ppn = total_discount * 0.11 + return { + 'total_discount': self.format_currency(total_discount), + 'total_voucher': self.format_currency(total_voucher), + 'subtotal_website': self.format_currency(subtotal_website), + 'ppn': self.format_currency(ppn), + 'grand_total': self.format_currency(total_discount + ppn) + } + return self.format_currency(0.0) + + def get_data_promo(self, program_line_id): + program_line_product = self.env['promotion.product'].search([ + ('program_line_id', '=', program_line_id) + ]) + return program_line_product + + def get_price_website(self, product_id): + price_website = self.env['product.pricelist.item'].search([('product_id', '=', product_id), ('pricelist_id', '=', 17022)], limit=1) + + price_tier = self.env['product.pricelist.item'].search([('product_id', '=', product_id), ('pricelist_id', '=', 17023)], limit=1) + + fixed_price = price_website.fixed_price if price_website else 0.0 + discount = price_tier.price_discount if price_tier else 0.0 + + discounted_price = fixed_price - (fixed_price * discount / 100) + + final_price = discounted_price / 1.11 + + return { + 'price': final_price, + 'web_price': discounted_price + } + + + def format_currency(self, number): number = int(number) return "{:,}".format(number).replace(',', '.')
\ No newline at end of file diff --git a/indoteknik_custom/models/x_banner_banner.py b/indoteknik_custom/models/x_banner_banner.py index d6884c9b..810bdf39 100755 --- a/indoteknik_custom/models/x_banner_banner.py +++ b/indoteknik_custom/models/x_banner_banner.py @@ -23,4 +23,6 @@ class XBannerBanner(models.Model): ('2', '2'), ('3', '3'), ('4', '4') - ], string='Group by Week')
\ No newline at end of file + ], string='Group by Week') + x_headline_banner = fields.Text(string="Headline Banner") + x_description_banner = fields.Text(string="Description Banner")
\ No newline at end of file diff --git a/indoteknik_custom/report/report_sale_order.xml b/indoteknik_custom/report/report_sale_order.xml index 595a989f..b9928790 100644 --- a/indoteknik_custom/report/report_sale_order.xml +++ b/indoteknik_custom/report/report_sale_order.xml @@ -7,7 +7,12 @@ <field name="report_type">qweb-pdf</field> <field name="report_name">indoteknik_custom.report_saleorder_website</field> <field name="report_file">indoteknik_custom.report_saleorder_website</field> - <field name="print_report_name">(object.state in ('draft', 'sent') and 'Quotation - %s' % (object.name)) or 'Order - %s' % (object.name)</field> + <field name="print_report_name"> + (object.state in ('draft', 'sent') and + ('Quotation - %s - %s - %s' % (object.partner_id.name, object.name, object.create_date.strftime('%d/%m/%Y'))) + or 'Order - %s - %s - %s' % (object.partner_id.name, object.name, object.create_date.strftime('%d/%m/%Y'))) + </field> + <field name="binding_model_id" ref="model_sale_order"/> <field name="binding_type">report</field> </record> diff --git a/indoteknik_custom/security/ir.model.access.csv b/indoteknik_custom/security/ir.model.access.csv index 597bb762..7731cc6f 100755 --- a/indoteknik_custom/security/ir.model.access.csv +++ b/indoteknik_custom/security/ir.model.access.csv @@ -131,3 +131,4 @@ access_report_logbook_bill_line,access.report.logbook.sj.line,model_report_logbo access_sale_order_multi_uangmuka_penjualan,access.sale.order.multi_uangmuka_penjualan,model_sale_order_multi_uangmuka_penjualan,,1,1,1,1 access_shipment_group,access.shipment.group,model_shipment_group,,1,1,1,1 access_shipment_group_line,access.shipment.group.line,model_shipment_group_line,,1,1,1,1 +access_sales_order_reject,access.sales.order.reject,model_sales_order_reject,,1,1,1,1 diff --git a/indoteknik_custom/views/customer_commision.xml b/indoteknik_custom/views/customer_commision.xml index 4b74cd34..0b72587e 100644 --- a/indoteknik_custom/views/customer_commision.xml +++ b/indoteknik_custom/views/customer_commision.xml @@ -27,6 +27,7 @@ <field name="state" readonly="1"/> <field name="product_id" readonly="1" optional="hide"/> <field name="dpp" readonly="1"/> + <field name="total_percent_margin" readonly="1"/> <field name="tax" readonly="1" optional="hide"/> <field name="total" readonly="1" optional="hide"/> </tree> diff --git a/indoteknik_custom/views/mail_template_invoice_po.xml b/indoteknik_custom/views/mail_template_invoice_po.xml new file mode 100644 index 00000000..04a946c0 --- /dev/null +++ b/indoteknik_custom/views/mail_template_invoice_po.xml @@ -0,0 +1,43 @@ +<?xml version="1.0" ?> +<odoo> + <data> + <record id="mail_template_invoice_po_document" model="mail.template"> + <field name="name">Purchase Orders: Send mail Invoice PO document</field> + <field name="model_id" ref="model_purchase_order" /> + <field name="subject">Your Invoice ${object.name}</field> + <field name="email_from">finance@indoteknik.co.id</field> + <field name="reply_to">finance@indoteknik.co.id</field> + <field name="report_template">375</field> + <field name="report_name">PO_${(object.name or '').replace('/','_')}</field> + <field name="email_to">${object.partner_id.email_finance|safe}</field> + <field name="email_cc">${object.partner_id.email_sales|safe}</field> + <field name="body_html" type="html"> + <p>Dengan Hormat Bpk/Ibu ${object.partner_id.name},</p> + <br/> + <p>Berikut terlampir pesanan pembelian <strong>${object.name}</strong> sebesar <strong>${format_amount(object.amount_total, object.currency_id)}</strong> dari PT. INDOTEKNIK DOTCOM GEMILANG.</p> + <p>Barang telah diterima pada tanggal ${format_date(object.date_done_picking)}.</p> + <br/> + <p>Tolong dibantu kirimkan softcopy invoice, surat jalan, dan faktur pajak atas PO terlampir dengan membalas email ini.</p> + <p>Dan kirimkan dokumen asli yang lengkap ke alamat :</p> + <p> + <strong>PT. INDOTEKNIK DOTCOM GEMILANG</strong><br/> + UP : FINANCE (021-2933-8828)<br/> + Gedung Altama Building : Jl. Bandengan Utara 85A No.8-9, RT.3/RW.16,<br/> + Penjaringan, Kec. Penjaringan, Jakarta Utara, DKI Jakarta 14440 + </p> + <br/> + <p>Terima kasih.</p> + <br/> + <br/> + <p>Best Regards, + <br/> + <br/> + <br/> + Dept. Finance<br/> + PT. INDOTEKNIK DOTCOM GEMILANG</p> + </field> + + <field name="auto_delete" eval="True" /> + </record> + </data> +</odoo> diff --git a/indoteknik_custom/views/product_pricelist.xml b/indoteknik_custom/views/product_pricelist.xml index 55139a24..0dfb69db 100644 --- a/indoteknik_custom/views/product_pricelist.xml +++ b/indoteknik_custom/views/product_pricelist.xml @@ -7,6 +7,7 @@ <field name="arch" type="xml"> <field name="company_id" position="after"> <field name="is_flash_sale"/> + <field name="is_show_program" attrs="{'invisible': [('is_flash_sale', '=', False)]}"/> </field> <page name="pricelist_rules" position="before"> <page name="flash_sale_setting" string="Flash Sale" attrs="{'invisible': [('is_flash_sale', '=', False)]}"> diff --git a/indoteknik_custom/views/promotion/promotion_program.xml b/indoteknik_custom/views/promotion/promotion_program.xml index 724f80c7..c9672b5a 100644 --- a/indoteknik_custom/views/promotion/promotion_program.xml +++ b/indoteknik_custom/views/promotion/promotion_program.xml @@ -71,4 +71,25 @@ sequence="1" action="promotion_program_action" /> + <record id="ir_actions_server_promotion_program_sync_to_solr" model="ir.actions.server"> + <field name="name">Sync to Solr</field> + <field name="model_id" ref="indoteknik_custom.model_promotion_program"/> + <field name="binding_model_id" ref="indoteknik_custom.model_promotion_program"/> + <field name="state">code</field> + <field name="code">model.action_sync_to_solr()</field> + </record> + <data noupdate="1"> + <record id="cron_program_solr_flag_solr" model="ir.cron"> + <field name="name">Program Promotion: Solr Flag to Queue</field> + <field name="interval_number">1</field> + <field name="interval_type">hours</field> + <field name="numbercall">-1</field> + <field name="doall" eval="False"/> + <field name="model_id" ref="model_promotion_program"/> + <field name="code">model.solr_flag_to_queue()</field> + <field name="state">code</field> + <field name="priority">55</field> + <field name="active">True</field> + </record> + </data> </odoo>
\ No newline at end of file diff --git a/indoteknik_custom/views/purchase_order.xml b/indoteknik_custom/views/purchase_order.xml index 08ab8691..142c13d8 100755 --- a/indoteknik_custom/views/purchase_order.xml +++ b/indoteknik_custom/views/purchase_order.xml @@ -46,11 +46,6 @@ <field name="total_so_margin"/> <field name="total_percent_margin"/> <field name="total_so_percent_margin"/> - <button name="re_calculate" - string="Re Calculate" - type="object" - attrs="{'invisible': [('approval_status', '=', 'approved')]}" - /> </field> <field name="product_id" position="before"> <field name="line_no" attrs="{'readonly': 1}" optional="hide"/> diff --git a/indoteknik_custom/views/res_partner.xml b/indoteknik_custom/views/res_partner.xml index fd8c8202..b928b046 100644 --- a/indoteknik_custom/views/res_partner.xml +++ b/indoteknik_custom/views/res_partner.xml @@ -36,6 +36,8 @@ <field name="leadtime"/> </group> <field name="vat" position="after"> + <field name="email_finance" widget="email"/> + <field name="email_sales" widget="email"/> <field name="use_so_approval" attrs="{'invisible': [('parent_id', '!=', False), ('company_type', '!=', 'company')]}" /> <field name="use_only_ready_stock" attrs="{'invisible': [('parent_id', '!=', False), ('company_type', '!=', 'company')]}" /> <field name="web_role" attrs="{'invisible': ['|', ('parent_id', '=', False), ('company_type', '=', 'company')]}" /> diff --git a/indoteknik_custom/views/sale_order.xml b/indoteknik_custom/views/sale_order.xml index 3eec6d3e..b8927a18 100755 --- a/indoteknik_custom/views/sale_order.xml +++ b/indoteknik_custom/views/sale_order.xml @@ -49,7 +49,7 @@ </field> <field name="user_id" position="after"> <field name="helper_by_id" readonly="1"/> - <field name="compute_fullfillment"/> + <field name="compute_fullfillment" invisible="1"/> </field> <field name="tag_ids" position="after"> <field name="eta_date" readonly="1"/> @@ -156,6 +156,7 @@ <field name="partner_purchase_order_name" readonly="True"/> <field name="partner_purchase_order_description" readonly="True"/> <field name="partner_purchase_order_file" readonly="True"/> + <field name="note_website" readonly="True"/> <field name="web_approval" readonly="True"/> </group> <group> @@ -192,6 +193,9 @@ <page string="Fullfillment" name="page_sale_order_fullfillment"> <field name="fullfillment_line" readonly="1"/> </page> + <page string="Reject Line" name="page_sale_order_reject_line"> + <field name="reject_line" readonly="1"/> + </page> </page> </field> </record> @@ -219,7 +223,10 @@ <field name="approval_status" /> <field name="client_order_ref"/> <field name="so_status"/> - <field name="date_doc_kirim" string="Tgl Kirim"/> + <field name="date_status_done"/> + <field name="date_kirim_ril"/> + <field name="date_driver_departure"/> + <field name="date_driver_arrival"/> <field name="payment_type" optional="hide"/> <field name="payment_status" optional="hide"/> </field> @@ -314,6 +321,20 @@ </data> <data> + <record id="sales_order_reject_tree" model="ir.ui.view"> + <field name="name">sales.order.reject.tree</field> + <field name="model">sales.order.reject</field> + <field name="arch" type="xml"> + <tree editable="top" create="false"> + <field name="product_id" readonly="1"/> + <field name="qty_reject" readonly="1"/> + <field name="reason_reject" readonly="1"/> + </tree> + </field> + </record> + </data> + + <data> <record id="sale_order_multi_create_uangmuka_ir_actions_server" model="ir.actions.server"> <field name="name">Uang Muka</field> <field name="model_id" ref="sale.model_sale_order"/> diff --git a/indoteknik_custom/views/shipment_group.xml b/indoteknik_custom/views/shipment_group.xml index b66bda3c..e9eec41b 100644 --- a/indoteknik_custom/views/shipment_group.xml +++ b/indoteknik_custom/views/shipment_group.xml @@ -19,6 +19,7 @@ <field name="picking_id" required="1"/> <field name="partner_id" readonly="1"/> <field name="sale_id" readonly="1"/> + <field name="shipping_paid_by" readonly="1"/> <field name="state" readonly="1"/> </tree> </field> diff --git a/indoteknik_custom/views/stock_picking.xml b/indoteknik_custom/views/stock_picking.xml index 4de1ac91..7567dda2 100644 --- a/indoteknik_custom/views/stock_picking.xml +++ b/indoteknik_custom/views/stock_picking.xml @@ -55,6 +55,16 @@ <field name="summary_qty_detail"/> <field name="count_line_detail"/> </field> + <field name="weight_uom_name" position="after"> + <group> + <group> + <button name="reset_status_printed" + string="Reset Status Printed" + type="object" + /> + </group> + </group> + </field> <field name="partner_id" position="after"> <field name="real_shipping_id"/> </field> @@ -75,6 +85,7 @@ <field name="group_id" position="before"> <field name="date_reserved"/> <field name="status_printed"/> + <field name="printed_sj"/> <field name="date_printed_sj"/> <field name="date_printed_list"/> <field name="is_internal_use" @@ -122,6 +133,18 @@ </field> </record> + <record id="view_stock_move_line_detailed_operation_tree_inherit" model="ir.ui.view"> + <field name="name">stock.move.line.operations.tree.inherit</field> + <field name="model">stock.move.line</field> + <field name="inherit_id" ref="stock.view_stock_move_line_detailed_operation_tree"/> + <field name="arch" type="xml"> + <tree editable="bottom" decoration-muted="(state == 'done' and is_locked == True)" decoration-danger="qty_done>product_uom_qty and state!='done' and parent.picking_type_code != 'incoming'" decoration-success="qty_done==product_uom_qty and state!='done' and not result_package_id"> + <field name="note" placeholder="Add a note here"/> + </tree> + </field> + </record> + + <record id="view_picking_internal_search_inherit" model="ir.ui.view"> <field name="name">stock.picking.internal.search.inherit</field> <field name="model">stock.picking</field> diff --git a/indoteknik_custom/views/website_user_cart.xml b/indoteknik_custom/views/website_user_cart.xml index e3630363..09ac7c67 100755 --- a/indoteknik_custom/views/website_user_cart.xml +++ b/indoteknik_custom/views/website_user_cart.xml @@ -16,6 +16,7 @@ <field name="program_line_id"/> <field name="qty"/> <field name="is_selected"/> + <field name="is_reminder"/> <field name="source"/> </tree> </field> @@ -34,6 +35,7 @@ <field name="program_line_id" /> <field name="qty" /> <field name="is_selected" /> + <field name="is_reminder" /> <field name="source" /> </group> <group></group> @@ -66,9 +68,10 @@ <record id="mail_template_user_cart_reminder_to_checkout" model="mail.template"> <field name="name">User Cart: Reminder to checkout</field> <field name="model_id" ref="indoteknik_custom.model_website_user_cart"/> - <field name="subject">Hello ${object.user_id.name}</field> - <field name="email_from">sales@indoteknik.com</field> - <field name="email_to">${object.user_id.login | safe}</field> + <field name="subject">Yuk, Checkout barang dikeranjang Kamu pakai Voucher Indoteknik</field> + <field name="email_from">noreply@indoteknik.com</field> + <field name="reply_to">sales@indoteknik.com</field> + <field name="email_to">${object.user_id.partner_id.email | safe}</field> <field name="body_html" type="html"> <table border="0" cellpadding="0" cellspacing="0" style="padding-top: 16px; background-color: #F1F1F1; font-family:Inter, Helvetica, Verdana, Arial,sans-serif; line-height: 24px; color: #454748; width: 100%; border-collapse:separate;"> <tr><td align="center"> @@ -79,8 +82,8 @@ <td align="center" style="min-width: 590px;"> <table border="0" cellpadding="0" cellspacing="0" width="590" style="min-width: 590px; background-color: white; padding: 0px 8px 0px 8px; border-collapse:separate;"> <tr> - <td valign="middle"> - <span></span> + <td valign="middle" align="center"> + <img src="https://erp.indoteknik.com/api/image/ir.attachment/datas/2135765" alt="Indoteknik" style="max-width: 100%; height: auto;"></img> </td> </tr> @@ -98,49 +101,141 @@ <table border="0" cellpadding="0" cellspacing="0" width="590" style="min-width: 590px; background-color: white; padding: 0px 8px 0px 8px; border-collapse:separate;"> <tr><td style="padding-bottom: 24px;">Halo ${object.user_id.name},</td></tr> - <tr><td style="padding-bottom: 16px;">Kami harap Anda dalam keadaan baik. Kami ingin mengingatkan Anda bahwa Anda memiliki beberapa produk yang masih ada di keranjang belanja Anda di situs kami, tetapi belum selesai untuk proses checkout.</td></tr> - <tr><td style="padding-bottom: 16px;">Jika Anda masih tertarik dengan produk-produk tersebut, jangan ragu untuk segera melanjutkan proses pembayaran. Ini adalah kesempatan Anda untuk mendapatkan barang-barang yang Anda inginkan sebelum kehabisan stok.</td></tr> - <tr><td style="padding-bottom: 16px;">Berikut adalah daftar produk yang masih ada di keranjang belanja Anda:</td></tr> - <!-- PRODUCT TABLE --> + <tr><td style="padding-bottom: 16px;">Produk yang kamu pilih masih menunggu dikeranjang belanja nih! Yuk, lakukan checkout barang pilihan kamu pakai voucher lebih hemat dan selesaikan transaksimu sekarang.</td></tr> + <tr><td style="padding-bottom: 16px;">Rasakan kemudahan transaksi lebih praktis hanya di Indoteknik.com</td></tr> + <tr><td style="padding-bottom: 16px;">Voucher untuk Produk Keranjangmu :</td></tr> + % set voucher = object.env['voucher'].browse(146) + % set discount_amount = object.env['website.user.cart'].calculate_discount(object.user_id.id) + % if voucher: <tr><td> - % set base_url = object.env['ir.config_parameter'].get_param('web.base.url') - % for cart in object.user_other_carts: - % set user_pricelist = cart.user_id.partner_id.property_product_pricelist - % set product_price = cart.product_id.calculate_website_price(pricelist=user_pricelist) - % set product_template_id = cart.product_id.product_tmpl_id.id - % set subtotal = product_price['price_discount'] * cart.qty - <a href="https://indoteknik.com/shop/cart"> - <table style="box-shadow: rgba(0, 0, 0, 0.16) 0px 1px 4px; width: 100%; margin-bottom: 12px; color: black !important;"> - <tbody> + <table border="1" style="width: 100%; margin-bottom: 12px;"> + <thead> + <tr> + <th>Kode Voucher</th> + <th>Jumlah Diskon</th> + </tr> + </thead> + <tbody> + <tr> + <td style="text-align: center;">${voucher.code}</td> + <td style="text-align: center;">Rp${discount_amount['total_voucher']}</td> + </tr> + </tbody> + </table> + </td></tr> + % endif + + <tr><td style="padding-bottom: 16px;">Berikut adalah daftar produk yang masih ada di keranjang belanja Anda:</td></tr> + <tr> + <td> + <!-- Tabel Produk --> + % set base_url = object.env['ir.config_parameter'].get_param('web.base.url') + <table style="box-shadow: rgba(0, 0, 0, 0.16) 0px 1px 4px; width: 100%; margin-bottom: 12px; color: black !important; border-collapse: collapse;"> + <thead> + <tr> + <th style="border: 1px solid #dddddd; padding: 8px;">Nama Produk</th> + <th style="border: 1px solid #dddddd; padding: 8px;">Harga Diskon</th> + <th style="border: 1px solid #dddddd; padding: 8px;">Jumlah</th> + <th style="border: 1px solid #dddddd; padding: 8px;">Subtotal</th> + </tr> + </thead> + <tbody> + % for cart in object.user_other_carts: + % set product_price = cart.get_price_website(cart.product_id.id) + % set product_template_id = cart.product_id.product_tmpl_id.id + % set subtotal = product_price['price'] * cart.qty <tr> - <td style="width: 120px; padding: 8px !important;"> - <img src="${base_url}api/image/product.template/image_512/${product_template_id}" alt="${cart.product_id.name}" width="120" height="120" style="object-fit: fill; object-position: center;"/> + % if cart.program_line_id: + % set promo = cart.get_data_promo(cart.program_line_id.id) + % set subtotal = promo[0].program_line_id.price * cart.qty + <td style="border: 1px solid #dddddd; padding: 8px;"> + <div style="text-align: left; margin-bottom: 16px;"> + <img src="${base_url}api/image/promotion.program/image/${promo[0].program_line_id.program_id.id}" alt="${promo[0].program_line_id.program_id.name}" width="120" height="120" style="object-fit: fill; object-position: center;"/> + </div> + <div style="display: block; gap: 16px; justify-content: center;"> + % for data in promo: + <div style="text-align: center;"> + <img src="${base_url}api/image/product.template/image_512/${data.product_id.product_tmpl_id.id}" alt="${data.product_id.name}" width="100" height="100" style="object-fit: fill; object-position: center;"/> + <div style="font-weight: 600; margin-top: 4px;">${data.product_id.name}</div> + </div> + % endfor + </div> + </td> + <td style="border: 1px solid #dddddd; padding: 8px;"> + <div>Rp${cart.format_currency(promo[0].program_line_id.price)}</div> + </td> + <td style="border: 1px solid #dddddd; padding: 8px;"> + <div>${'%d pcs' % cart.qty}</div> </td> - <td style="vertical-align: top; padding: 8px;"> - <div style="font-weight: 600;">${cart.product_id.name}</div> - % if product_price['discount_percentage'] != 0: - <div><span style="text-decoration: line-through;">Rp${cart.format_currency(product_price['price'])}</span> (${'%d' % product_price['discount_percentage']}%)</div> - % endif - <div>Rp${cart.format_currency(product_price['price_discount'])} x ${'%d pcs' % cart.qty}</div> + <td style="border: 1px solid #dddddd; padding: 8px;"> <div style="font-weight: 600;">Rp${cart.format_currency(subtotal)}</div> </td> + % else + <td style="border: 1px solid #dddddd; padding: 8px;"> + <img src="${base_url}api/image/product.template/image_512/${product_template_id}" alt="${cart.product_id.name}" width="120" height="120" style="object-fit: fill; object-position: center;"/> + <div style="font-weight: 600;">${cart.product_id.name}</div> + </td> + <td style="border: 1px solid #dddddd; padding: 8px;"> + <div>Rp${cart.format_currency(product_price['price'])}</div> + </td> + <td style="border: 1px solid #dddddd; padding: 8px;"> + <div>${'%d pcs' % cart.qty}</div> + </td> + <td style="border: 1px solid #dddddd; padding: 8px;"> + <div style="font-weight: 600;">Rp${cart.format_currency(subtotal)}</div> + </td> + %endif </tr> - </tbody> - </table> - </a> - % endfor - </td></tr> + % endfor + </tbody> + </table> + % set totalan = object.calculate_discount(object.user_id.id) + + <table style="width: 100%; margin-top: 12px; color: black !important; border-collapse: collapse;"> + <tbody> + <tr> + <td style="text-align: right; padding: 8px;"><b>Subtotal:</b></td> + <td style="text-align: right; padding: 8px;">Rp${totalan['subtotal_website']}</td> + </tr> + <tr> + <td style="text-align: right; padding: 8px;"><b>Pakai Voucher:</b></td> + <td style="text-align: right; padding: 8px; color: green;"><b>(Potensi Potongan)</b> <b>Rp${totalan['total_voucher']}</b></td> + </tr> + <tr> + <td style="text-align: right; padding: 8px;"><b>Total:</b></td> + <td style="text-align: right; padding: 8px;">Rp${totalan['total_discount']}</td> + </tr> + <tr> + <td style="text-align: right; padding: 8px;"><b>PPN (11%):</b></td> + <td style="text-align: right; padding: 8px;">Rp${totalan['ppn']}</td> + </tr> + <tr> + <td style="text-align: right; padding: 8px;"><b>Grand Total:</b></td> + <td style="text-align: right; padding: 8px;"><b>Rp${totalan['grand_total']}</b></td> + </tr> + </tbody> + </table> + </td> + </tr> + + <tr> + <td style="text-align: center; padding: 20px;"> + <a href="https://indoteknik.com/shop/cart" style="background-color: #FFC107; color: black; padding: 10px 20px; text-decoration: none; border-radius: 4px; display: inline-block;">Cek Keranjang</a> + <a href="https://indoteknik.com/shop/checkout?voucher=PASTIHEMAT" style="background-color: #FF0000; color: white; padding: 10px 20px; text-decoration: none; border-radius: 4px; margin-left: 10px; display: inline-block;">Bayar Sekarang</a> + </td> + </tr> - <tr><td style="padding-bottom: 16px;">Kami juga ingin memberitahu Anda bahwa kami menyediakan layanan pelanggan yang siap membantu jika Anda memiliki pertanyaan atau memerlukan bantuan dalam proses pembayaran. Jangan ragu untuk menghubungi kami melalui email ini atau nomor layanan pelanggan kami yang tertera di situs.</td></tr> - <tr><td style="padding-bottom: 16px;">Terima kasih atas perhatian Anda dan kesempatan untuk melayani Anda. Kami berharap dapat segera melihat Anda menyelesaikan pembelian Anda.</td></tr> + <tr><td style="padding-bottom: 16px;">Terima kasih atas perhatian Anda dan kesempatan untuk melayani Anda. Kami berharap dapat segera melihat Anda menyelesaikan pembelian Anda. Untuk pertanyaan lebih lanjut dapat menghubungi nomor Whatsapp Resmi kami di <a href="https://api.whatsapp.com/send?phone=6281717181922">0817-1718-1922</a></td></tr> <tr><td style="padding-bottom: 2px;">Hormat kami,</td></tr> <tr><td style="padding-bottom: 2px;">PT. Indoteknik Dotcom Gemilang</td></tr> <tr><td style="padding-bottom: 2px;">sales@indoteknik.com</td></tr> + + <tr><td style="padding-bottom: 2px;">Email ini dibuat secara otomatis. Mohon tidak mengirimkan balasan ke email ini.</td></tr> <tr> <td style="text-align:center;"> <hr width="100%" - style="background-color:rgb(204,204,204);border:medium none;clear:both;display:block;font-size:0px;min-height:1px;line-height:0; margin: 16px 0px 16px 0px;" /> + style="background-color:rgb(204,204,204);border:medium none;clear:both;display:block;font-size:0px;min-height:1px;line-height:0; margin: 16px 0px 16px 0px;" /> </td> </tr> </table> diff --git a/indoteknik_custom/views/x_banner_banner.xml b/indoteknik_custom/views/x_banner_banner.xml index c90e718b..ec1e38a5 100755 --- a/indoteknik_custom/views/x_banner_banner.xml +++ b/indoteknik_custom/views/x_banner_banner.xml @@ -31,6 +31,8 @@ <field name="x_status_banner" /> <field name="sequence" /> <field name="group_by_week" /> + <field name="x_headline_banner" /> + <field name="x_description_banner" /> <field name="last_update_solr" readonly="1"/> </group> <group> |
