summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAzka Nathan <darizkyfaz@gmail.com>2025-07-04 15:23:18 +0700
committerAzka Nathan <darizkyfaz@gmail.com>2025-07-04 15:23:18 +0700
commit0b16acf14c58fe224557d843101b256f7028fb5b (patch)
tree8bdccd2e80072fb28dcaf879fd1a5d704cb8498b
parentdd93d36b4c7cec4e1681d69e296a7cff559386a0 (diff)
shipment group
-rwxr-xr-xfixco_custom/__manifest__.py2
-rwxr-xr-xfixco_custom/models/__init__.py1
-rw-r--r--fixco_custom/models/requisition.py383
-rw-r--r--fixco_custom/models/shipment_group.py149
-rwxr-xr-xfixco_custom/security/ir.model.access.csv7
-rw-r--r--fixco_custom/views/requisition.xml126
-rw-r--r--fixco_custom/views/shipment_group.xml25
-rw-r--r--fixco_custom/views/shipment_line.xml109
8 files changed, 792 insertions, 10 deletions
diff --git a/fixco_custom/__manifest__.py b/fixco_custom/__manifest__.py
index 53df90a..9c943e0 100755
--- a/fixco_custom/__manifest__.py
+++ b/fixco_custom/__manifest__.py
@@ -32,6 +32,8 @@
'views/upload_ginee.xml',
'views/report_picking_list.xml',
'views/purchase_order.xml',
+ 'views/requisition.xml',
+ 'views/shipment_line.xml',
],
'demo': [],
'css': [],
diff --git a/fixco_custom/models/__init__.py b/fixco_custom/models/__init__.py
index 226e6b9..4bc9e44 100755
--- a/fixco_custom/models/__init__.py
+++ b/fixco_custom/models/__init__.py
@@ -18,3 +18,4 @@ from . import uangmuka_penjualan
from . import upload_ginee
from . import purchase_order_line
from . import purchase_order
+from . import requisition
diff --git a/fixco_custom/models/requisition.py b/fixco_custom/models/requisition.py
new file mode 100644
index 0000000..1734abd
--- /dev/null
+++ b/fixco_custom/models/requisition.py
@@ -0,0 +1,383 @@
+from odoo import models, fields, api, tools, _
+from odoo.exceptions import UserError
+from datetime import datetime
+import math
+import logging
+
+_logger = logging.getLogger(__name__)
+
+
+class RequisitionMatchPO(models.Model):
+ _name = 'v.requisition.match.po'
+ _auto = False
+ _rec_name = 'product_id'
+
+ id = fields.Integer(string='ID')
+ product_id = fields.Many2one('product.product', string='Product')
+ qty_rpo = fields.Float(string='Qty RPO', help='Qty RPO yang sudah di PO namun SO masih Draft')
+
+ # def init(self):
+ # tools.drop_view_if_exists(self.env.cr, self._table)
+ # self.env.cr.execute("""
+ # create or replace view %s as
+ # select rl.product_id as id, rl.product_id, sum(rl.qty_purchase) as qty_rpo
+ # from requisition_line rl
+ # join requisition r on r.id = rl.requisition_id
+ # join requisition_purchase_match rpm on rpm.requisition_id = r.id
+ # join purchase_order po on po.id = rpm.order_id
+ # join sale_order so on so.id = r.sale_order_id
+ # where 1=1
+ # and r.date_doc >= '2024-11-11'
+ # and po.state in ('done', 'purchase')
+ # and so.state in ('draft', 'sent')
+ # group by rl.product_id
+ # """ % self._table)
+
+
+class Requisition(models.Model):
+ _name = 'requisition'
+ _order = 'id desc'
+ _inherit = ['mail.thread']
+ _rec_name = 'number'
+
+ number = fields.Char(string='Document No', index=True, copy=False, readonly=True)
+ date_doc = fields.Date(string='Date', help='isi tanggal hari ini')
+ description = fields.Char(string='Description', help='bebas isinya apa aja')
+ requisition_lines = fields.One2many('requisition.line', 'requisition_id', string='Lines', auto_join=True)
+ notification = fields.Char(string='Notification')
+ is_po = fields.Boolean(string='Is PO')
+ requisition_match = fields.One2many('requisition.purchase.match', 'requisition_id', string='Matches', auto_join=True)
+ sale_order_id = fields.Many2one('sale.order', string='SO', help='harus diisi nomor SO yang ingin digenerate')
+ sales_approve = fields.Boolean(string='Approval Status', tracking=3, copy=False)
+ merchandise_approve = fields.Boolean(string='Approval Status', tracking=3, copy=False)
+
+ def generate_requisition_from_so(self):
+ state = ['done', 'sale']
+ if not self.sale_order_id:
+ raise UserError('Sale Order Wajib Diisi dan Harus Draft')
+ if self.sale_order_id.state in state:
+ raise UserError('SO sudah Confirm, akan berakibat double Purchase melalui PJ')
+ if not self.sale_order_id.order_line:
+ raise UserError('Line SO masih kosong, harus diisi dulu')
+ for order_line in self.sale_order_id.order_line:
+ param = {
+ 'requisition_id': self.id,
+ 'product_id': order_line.product_id.id,
+ 'partner_id': order_line.vendor_id.id,
+ 'qty_purchase': order_line.product_uom_qty,
+ 'price_unit': order_line.purchase_price,
+ 'taxes_id': order_line.purchase_tax_id.id,
+ 'subtotal': order_line.purchase_price * order_line.product_uom_qty,
+ 'brand_id': order_line.product_id.x_manufacture.id
+ }
+ self.env['requisition.line'].create([param])
+
+ @api.model
+ def create(self, vals):
+ vals['number'] = self.env['ir.sequence'].next_by_code('requisition') or '0'
+ result = super(Requisition, self).create(vals)
+ return result
+
+ def button_approve(self):
+ state = ['done', 'sale']
+ if self.sale_order_id.state in state:
+ raise UserError('SO sudah Confirm, akan berakibat double Purchase melalui PJ')
+ if self.env.user.id not in [21, 19, 28]:
+ raise UserError('Hanya Rafly dan Darren Yang Bisa Approve')
+ if self.env.user.id == 19 or self.env.user.id == 28:
+ self.sales_approve = True
+ elif self.env.user.id == 21 or self.env.user.id == 28:
+ self.merchandise_approve = True
+
+ def create_po_from_requisition(self):
+ if not self.sales_approve and not self.merchandise_approve:
+ raise UserError('Harus Di Approve oleh Darren atau Rafly')
+ if not self.requisition_lines:
+ raise UserError('Tidak ada Lines, belum bisa create PO')
+ if self.is_po:
+ raise UserError('Sudah pernah di create PO')
+ if not self.sale_order_id:
+ raise UserError('Tidak ada link dengan Sales Order, tidak bisa dihitung sebagai Plafon Qty di PO')
+
+ vendor_ids = self.env['requisition.line'].read_group([
+ ('requisition_id', '=', self.id),
+ ('partner_id', '!=', False)
+ ], fields=['partner_id'], groupby=['partner_id'])
+
+ po_ids = []
+ for vendor in vendor_ids:
+ result_po = self.create_po_by_vendor(vendor['partner_id'][0])
+ po_ids += result_po
+ return {
+ 'name': _('Purchase Order'),
+ 'view_mode': 'tree,form',
+ 'res_model': 'purchase.order',
+ 'target': 'current',
+ 'type': 'ir.actions.act_window',
+ 'domain': [('id', 'in', po_ids)],
+ }
+
+ def create_po_by_vendor(self, vendor_id):
+ current_time = datetime.now()
+
+ PRODUCT_PER_PO = 20
+
+ requisition_line = self.env['requisition.line']
+
+ param_header = {
+ 'partner_id': vendor_id,
+ # 'partner_ref': self.sale_order_id.name,
+ 'currency_id': 12,
+ 'user_id': self.env.user.id,
+ 'company_id': 1, # indoteknik dotcom gemilang
+ 'picking_type_id': 28, # indoteknik bandengan receipts
+ 'date_order': current_time,
+ 'sale_order_id': self.sale_order_id.id,
+ 'note_description': 'from Purchase Requisition'
+ }
+
+ domain = [
+ ('requisition_id', '=', self.id),
+ ('partner_id', '=', vendor_id),
+ ('qty_purchase', '>', 0)
+ ]
+
+ products_len = requisition_line.search_count(domain)
+ page = math.ceil(products_len / PRODUCT_PER_PO)
+ po_ids = []
+ # i start from zero (0)
+ for i in range(page):
+ new_po = self.env['purchase.order'].create([param_header])
+ new_po.name = new_po.name + "/R/" + str(i + 1)
+ po_ids.append(new_po.id)
+ lines = requisition_line.search(
+ domain,
+ offset=i * PRODUCT_PER_PO,
+ limit=PRODUCT_PER_PO
+ )
+ tax = [22]
+
+ for line in lines:
+ product = line.product_id
+ param_line = {
+ 'order_id' : new_po.id,
+ 'product_id': product.id,
+ 'product_qty': line.qty_purchase,
+ 'product_uom_qty': line.qty_purchase,
+ 'name': product.display_name,
+ 'price_unit': line.price_unit,
+ 'taxes_id': tax,
+ }
+ new_po_line = self.env['purchase.order.line'].create([param_line])
+ line.current_po_id = new_po.id
+ line.current_po_line_id = new_po_line.id
+
+ self.env['requisition.purchase.match'].create([{
+ 'requisition_id': self.id,
+ 'order_id': new_po.id
+ }])
+ self.is_po = True
+
+ return po_ids
+
+ # def create_po_from_requisition(self):
+ # if not self.requisition_lines:
+ # raise UserError('Tidak ada Lines, belum bisa create PO')
+ # if self.is_po:
+ # raise UserError('Sudah pernah di create PO')
+ # current_time = datetime.now()
+ # vendor_ids = self.env['requisition.line'].read_group([('requisition_id', '=', self.id), ('partner_id', '!=', False)], fields=['partner_id'], groupby=['partner_id'])
+
+ # counter_po_number = 0
+ # po_ids = []
+ # for vendor in vendor_ids:
+ # param_header = {
+ # 'partner_id': vendor['partner_id'][0],
+ # # 'partner_ref': self.sale_order_id.name,
+ # 'currency_id': 12,
+ # 'user_id': self.env.user.id,
+ # 'company_id': 1, # indoteknik dotcom gemilang
+ # 'picking_type_id': 28, # indoteknik bandengan receipts
+ # 'date_order': current_time,
+ # 'sale_order_id': self.sale_order_id.id,
+ # 'note_description': 'from Purchase Requisition'
+ # }
+ # param_requisition_line = [
+ # ('requisition_id', '=', self.id),
+ # ('partner_id', '=', vendor['partner_id'][0]),
+ # ('qty_purchase', '>', 0)
+ # ]
+ # # new_po = self.env['purchase.order'].create([param_header])
+ # products_vendors = self.env['requisition.line'].search(, order='brand_id')
+ # count = brand_id = 0
+
+ # for product in products_vendors:
+ # if count > 200 or brand_id != product.brand_id.id:
+ # continue
+
+ # count = 0
+ # counter_po_number += 1
+ # new_po = self.env['purchase.order'].create([param_header])
+ # new_po.name = new_po.name + "/R/"+str(counter_po_number)
+ # self.env['requisition.purchase.match'].create([{
+ # 'requisition_id': self.id,
+ # 'order_id': new_po.id
+ # }])
+ # po_ids.append(new_po.id)
+ # self.env.cr.commit()
+ # # else:
+ # # new_po = self.env['purchase.order'].create([param_header])
+ # brand_id = product.brand_id.id
+ # count += 10
+
+ # # qty_available = product.product_id.qty_onhand_bandengan + product.product_id.qty_incoming_bandengan - product.product_id.outgoing_qty
+ # # suggest = 'harus beli'
+ # # if qty_available > product.qty_purchase:
+ # # suggest = 'masih cukup'
+
+ # tax = [22]
+
+ # param_line = {
+
+ # 'sequence': count,
+ # 'product_id': product.product_id.id,
+ # 'product_qty': product.qty_purchase,
+ # 'product_uom_qty': product.qty_purchase,
+ # 'price_unit': product.price_unit,
+ # 'taxes_id': tax,
+ # # 'qty_available_store': qty_available,
+ # # 'suggest': suggest,
+ # }
+ # new_line = self.env['purchase.order.line'].create([param_line])
+ # if new_po:
+ # new_line.write({
+ # 'order_id': new_po.id,
+ # })
+ # product.current_po_id = new_po.id
+ # product.current_po_line_id = new_line.id
+ # _logger.info('Create PO Line %s' % product.product_id.name)
+ # # self.notification = self.notification + ' %s' % new_po.name
+ # self.is_po = True
+ # if po_ids:
+ # return {
+ # 'name': _('Purchase Order'),
+ # 'view_mode': 'tree,form',
+ # 'res_model': 'purchase.order',
+ # 'target': 'current',
+ # 'type': 'ir.actions.act_window',
+ # 'domain': [('id', 'in', po_ids)],
+ # }
+
+class RequisitionLine(models.Model):
+ _name = 'requisition.line'
+ _description = 'Requisition Line'
+ _order = 'requisition_id, id'
+ _inherit = ['mail.thread']
+
+ requisition_id = fields.Many2one('requisition', string='Ref', required=True, ondelete='cascade', index=True, copy=False)
+ brand_id = fields.Many2one('x_manufactures', string='Brand')
+ product_id = fields.Many2one('product.product', string='Product', tracking=3,)
+ partner_id = fields.Many2one('res.partner', string='Vendor')
+ qty_purchase = fields.Float(string='Qty Purchase')
+ price_unit = fields.Float(string='Price')
+ tax_id = fields.Many2one('account.tax', help='isi tax pembelian include atau exclude', domain="[('type_tax_use', '=', 'purchase')]")
+ subtotal = fields.Float(string='Subtotal')
+ last_price = fields.Float(string='Last Price')
+ last_order_id = fields.Many2one('purchase.order', string='Last Order')
+ last_orderline_id = fields.Many2one('purchase.order.line', string='Last Order Line')
+ taxes_id = fields.Many2one('account.tax', string='Tax')
+ is_po = fields.Boolean(String='Is PO')
+ current_po_id = fields.Many2one('purchase.order', string='Current')
+ current_po_line_id = fields.Many2one('purchase.order.line', string='Current Line')
+ source = fields.Char(string='Source', help='data harga diambil darimana')
+ qty_available_store = fields.Float(string='Available')
+ suggest = fields.Char(string='Suggest')
+
+ def _get_valid_purchase_price(self, purchase_price):
+ price = 0
+ taxes = 24
+ human_last_update = purchase_price.human_last_update or datetime.min
+ system_last_update = purchase_price.system_last_update or datetime.min
+
+ #if purchase_price.taxes_product_id.type_tax_use == 'purchase':
+ price = purchase_price.product_price
+ taxes = purchase_price.taxes_product_id.id or 24
+
+ if system_last_update > human_last_update:
+ #if purchase_price.taxes_system_id.type_tax_use == 'purchase':
+ price = purchase_price.system_price
+ taxes = purchase_price.taxes_system_id.id or 24
+
+ return price, taxes
+
+ @api.onchange('price_unit')
+ def _onchange_price_unit(self):
+ for line in self:
+ line.subtotal = line.price_unit * line.qty_purchase
+
+ @api.onchange('product_id')
+ def _onchange_product(self):
+ for line in self:
+ line.brand_id = line.product_id.product_tmpl_id.x_manufacture.id
+ purchase_pricelist = self.env['purchase.pricelist'].search([
+ ('product_id', '=', line.product_id.id)
+ ],order='count_trx_po desc, count_trx_po_vendor desc', limit=1)
+
+ price, taxes = line._get_valid_purchase_price(purchase_pricelist)
+ line.price_unit = price
+ line.taxes_id = taxes
+ line.partner_id = purchase_pricelist.vendor_id.id
+
+ @api.model
+ def create(self, vals):
+ record = super(RequisitionLine, self).create(vals)
+ record._track_changes('Tambah')
+ return record
+
+ def write(self, vals):
+ for record in self:
+ old_values = {field: record[field] for field in vals if field in record}
+
+ result = super(RequisitionLine, self).write(vals)
+
+ for record in self:
+ record._track_changes('Updated', old_values)
+
+ return result
+
+ def unlink(self):
+ for record in self:
+ record._track_changes('Hapus')
+ return super(RequisitionLine, self).unlink()
+
+ def _track_changes(self, action, old_values=None):
+ message = f"Produk telah di-{action} : <br/>"
+ if action == 'Tambah':
+ # message += f"<br/> Product: {self.product_id.name}"
+ message += f"Product: {self.product_id.name} <br/> Vendor: {self.partner_id.name} <br/> Qty: {self.qty_purchase} <br/> Price: {self.price_unit} <br/> Tax: {self.tax_id.name} <br/> Subtotal: {self.subtotal} <br/> Brand: {self.brand_id.x_name}"
+ elif action == 'Hapus':
+ # message += f"<br/> Deleted Product: {self.product_id.name}"
+ message += f"<br/> Deleted Product: {self.product_id.name} <br/> Vendor: {self.partner_id.name} Qty: {self.qty_purchase} <br/> Price: {self.price_unit} <br/> Tax: {self.tax_id.name} <br/> Subtotal: {self.subtotal} <br/> Brand: {self.brand_id.x_name}"
+ else: # Updated
+ for field, old_value in old_values.items():
+ new_value = self[field]
+ if old_value != new_value:
+ field_label = self._fields[field].string # Ambil nama label field
+ message += f"{field_label}: {old_value} -> {new_value}<br/>"
+
+ if self.requisition_id:
+ self.requisition_id.message_post(body=message)
+
+class RequisitionPurchaseMatch(models.Model):
+ _name = 'requisition.purchase.match'
+ _order = 'requisition_id, id'
+
+ requisition_id = fields.Many2one('requisition', string='Ref', required=True, ondelete='cascade', index=True, copy=False)
+ order_id = fields.Many2one('purchase.order', string='Purchase Order')
+ vendor = fields.Char(string='Vendor', compute='_compute_info_po')
+ total = fields.Float(string='Total', compute='_compute_info_po')
+
+ def _compute_info_po(self):
+ for match in self:
+ match.vendor = match.order_id.partner_id.name
+ match.total = match.order_id.amount_total
diff --git a/fixco_custom/models/shipment_group.py b/fixco_custom/models/shipment_group.py
index 3d60840..228306f 100644
--- a/fixco_custom/models/shipment_group.py
+++ b/fixco_custom/models/shipment_group.py
@@ -1,10 +1,29 @@
-from odoo import models, api, fields
+from odoo import fields, models, api, _
from odoo.exceptions import AccessError, UserError, ValidationError
-from datetime import timedelta, date
+from odoo.tools.float_utils import float_is_zero
+from collections import defaultdict
+from datetime import timedelta, datetime
+from datetime import timedelta, datetime as waktu
+from itertools import groupby
+import pytz, requests, json, requests
+from dateutil import parser
+import datetime
+import hmac
+import hashlib
+import base64
+import requests
+import time
import logging
+import re
+from hashlib import sha256
_logger = logging.getLogger(__name__)
+
+Request_URI = '/openapi/order/v2/list-order'
+ACCESS_KEY = '24bb6a1ec618ec6a'
+SECRET_KEY = '32e4a78ad05ee230'
+
class ShipmentGroup(models.Model):
_name = "shipment.group"
_description = "Shipment Group"
@@ -14,11 +33,56 @@ class ShipmentGroup(models.Model):
number = fields.Char(string='Document No', index=True, copy=False, readonly=True, tracking=True)
shipment_line = fields.One2many('shipment.group.line', 'shipment_id', string='Shipment Group Lines', auto_join=True)
picking_lines = fields.One2many('picking.line', 'shipment_id', string='Picking Lines', auto_join=True)
+ related_count = fields.Integer(compute='_compute_related_count', string='Related Count')
+
+ def sync_product_to_picking_line(self):
+ for shipment in self:
+ for picking_line in shipment.picking_lines:
+ shipment_lines = self.env['shipment.group.line'].search([
+ ('shipment_id', '=', shipment.id),
+ ('invoice_marketplace', '=', picking_line.invoice_marketplace),
+ ('picking_id', '=', picking_line.picking_id.id)
+ ])
+
+ picking_line.product_shipment_lines.unlink()
+
+ for sl in shipment_lines:
+ self.env['product.shipment.line'].create({
+ 'picking_line_id': picking_line.id,
+ 'product_id': sl.product_id.id,
+ 'carrier': sl.carrier or picking_line.carrier,
+ 'invoice_marketplace': sl.invoice_marketplace,
+ 'picking_id': sl.picking_id.id,
+ 'order_reference': sl.order_reference,
+ })
+
+ def _compute_related_count(self):
+ for record in self:
+ record.related_count = len(record.picking_lines)
+
+ def action_view_related_line(self):
+ self.ensure_one()
+
+ pickingLines = self.env['picking.line']
+
+ base_bu = pickingLines.search([
+ ('shipment_id', '=', self.id)
+ ])
+
+ all_bu = base_bu
+
+ return {
+ 'name': 'Related',
+ 'type': 'ir.actions.act_window',
+ 'res_model': 'picking.line',
+ 'view_mode': 'tree,form',
+ 'target': 'current',
+ 'domain': [('id', 'in', list(all_bu.ids))],
+ }
@api.constrains('picking_lines')
def _check_picking_lines(self):
for record in self:
- record.shipment_line.unlink()
for picking_line in record.picking_lines:
if not picking_line.picking_id:
@@ -37,8 +101,59 @@ class ShipmentGroup(models.Model):
'product_id': move.product_id.id,
'carrier': picking_line.picking_id.carrier,
'invoice_marketplace': picking_line.picking_id.invoice_mp,
+ 'order_reference': picking_line.order_reference,
'picking_id': picking_line.picking_id.id
})
+
+ def get_status(self):
+ for picking_line in self.picking_lines:
+ try:
+ order_id = picking_line.invoice_marketplace
+
+ authorization = self.sign_request()
+ headers = {
+ 'Content-Type': 'application/json',
+ 'X-Advai-Country': 'ID',
+ 'Authorization': authorization
+ }
+ payload = {
+ "orderNumbers": [order_id],
+ }
+ url = "https://api.ginee.com/openapi/order/v2/list-order"
+
+ response = requests.post(
+ url,
+ headers=headers,
+ data=json.dumps(payload)
+ )
+
+ if response.status_code == 200:
+ data = response.json()
+ if data.get('code') == 'SUCCESS' and data.get('message') == 'OK':
+ content = data.get('data', {}).get('content', [])
+
+ if not content:
+ raise UserError(_("No List Order information found in response"))
+
+ content_info = content[0]
+ if content_info.get('orderStatus') == 'CANCELLED':
+ picking_line.status = content_info.get('orderStatus')
+ else:
+ picking_line.status = ''
+ else:
+ raise UserError(_("API Error: %s - %s") % (data.get('code', 'UNKNOWN'), data.get('message', 'No error message')))
+ else:
+ raise UserError(_("API request failed with status code: %s") % response.status_code)
+
+ except Exception as e:
+ raise UserError(_("Error: %s") % str(e))
+
+ def sign_request(self):
+ signData = '$'.join(['POST', Request_URI]) + '$'
+ authorization = ACCESS_KEY + ':' + base64.b64encode(
+ hmac.new(SECRET_KEY.encode('utf-8'), signData.encode('utf-8'), digestmod=sha256).digest()
+ ).decode('ascii')
+ return authorization
@api.model
@@ -57,6 +172,7 @@ class ShipmentGroupLine(models.Model):
carrier = fields.Char(string='Shipping Method')
invoice_marketplace = fields.Char(string='Invoice Marketplace')
picking_id = fields.Many2one('stock.picking', string='Picking')
+ order_reference = fields.Char(string='Order Reference')
class PickingLine(models.Model):
@@ -65,16 +181,33 @@ class PickingLine(models.Model):
_order = 'shipment_id, id'
shipment_id = fields.Many2one('shipment.group', string='Shipment Ref', required=True, ondelete='cascade')
+ product_shipment_lines = fields.One2many('product.shipment.line', 'picking_line_id', string='Product Shipment Lines', auto_join=True)
picking_id = fields.Many2one('stock.picking', string='Picking')
- scan_invoice_marketplace = fields.Char(string="Scan Invoice Marketplace")
+ scan_receipt = fields.Char(string="Scan Receipt")
+ invoice_marketplace = fields.Char(string='Invoice Marketplace')
carrier = fields.Char(string='Ekspedisi')
+ order_reference = fields.Char(string='Order Reference')
+ status = fields.Char(string='Status')
- @api.onchange('scan_invoice_marketplace')
- def _onchange_scan_invoice_marketplace(self):
+ @api.onchange('scan_receipt')
+ def _onchange_scan_receipt(self):
for line in self:
- if line.scan_invoice_marketplace:
- picking = self.env['stock.picking'].search([('invoice_mp', '=', line.scan_invoice_marketplace)], limit=1)
+ if line.scan_receipt:
+ picking = self.env['stock.picking'].search([('tracking_number', '=', line.scan_receipt)], limit=1)
line.picking_id = picking.id
line.carrier = picking.carrier
+ line.order_reference = picking.order_reference
+ line.invoice_marketplace = picking.invoice_mp
+
+class ProductShipmentLine(models.Model):
+ _name = 'product.shipment.line'
+ _description = 'Product Shipment Line'
+ _order = 'picking_line_id, id'
+ picking_line_id = fields.Many2one('picking.line', string='Picking Line', required=True, ondelete='cascade')
+ product_id = fields.Many2one('product.product', string='Product')
+ carrier = fields.Char(string='Shipping Method')
+ invoice_marketplace = fields.Char(string='Invoice Marketplace')
+ picking_id = fields.Many2one('stock.picking', string='Picking')
+ order_reference = fields.Char(string='Order Reference')
diff --git a/fixco_custom/security/ir.model.access.csv b/fixco_custom/security/ir.model.access.csv
index 1fa0a59..14a8780 100755
--- a/fixco_custom/security/ir.model.access.csv
+++ b/fixco_custom/security/ir.model.access.csv
@@ -21,4 +21,9 @@ access_stock_picking_print_picking_list,access.stock.picking.print_picking_list,
access_uangmuka_penjualan,access.uangmuka.penjualan,model_uangmuka_penjualan,,1,1,1,1
access_picking_line,access.picking.line,model_picking_line,,1,1,1,1
access_upload_ginee,access.upload.ginee,model_upload_ginee,,1,1,1,1
-access_upload_ginee_line,access.upload.ginee.line,model_upload_ginee_line,,1,1,1,1 \ No newline at end of file
+access_upload_ginee_line,access.upload.ginee.line,model_upload_ginee_line,,1,1,1,1
+access_requisition,access.requisition,model_requisition,,1,1,1,1
+access_requisition_line,access.requisition.line,model_requisition_line,,1,1,1,1
+access_requisition_purchase_match,access.requisition.purchase.match,model_requisition_purchase_match,,1,1,1,1
+access_v_requisition_match_po,access.v.requisition.match.po,model_v_requisition_match_po,,1,1,1,1
+access_product_shipment_line,access.product.shipment.line,model_product_shipment_line,,1,1,1,1 \ No newline at end of file
diff --git a/fixco_custom/views/requisition.xml b/fixco_custom/views/requisition.xml
new file mode 100644
index 0000000..957113a
--- /dev/null
+++ b/fixco_custom/views/requisition.xml
@@ -0,0 +1,126 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<odoo>
+ <record id="requisition_tree" model="ir.ui.view">
+ <field name="name">requisition.tree</field>
+ <field name="model">requisition</field>
+ <field name="arch" type="xml">
+ <tree>
+ <field name="date_doc"/>
+ <field name="number"/>
+ <field name="description"/>
+ <field name="notification" readonly="1"/>
+ <field name="is_po" readonly="1"/>
+ </tree>
+ </field>
+ </record>
+
+ <record id="requisition_line_tree" model="ir.ui.view">
+ <field name="name">requisition.line.tree</field>
+ <field name="model">requisition.line</field>
+ <field name="arch" type="xml">
+ <tree>
+ <field name="brand_id"/>
+ <field name="product_id"/>
+ <field name="partner_id"/>
+ <field name="qty_purchase"/>
+ <field name="price_unit"/>
+ <field name="tax_id"/>
+ <field name="subtotal"/>
+ <field name="source"/>
+ <field name="qty_available_store"/>
+ <field name="suggest"/>
+ </tree>
+ </field>
+ </record>
+
+ <record id="requisition_purchase_match_tree" model="ir.ui.view">
+ <field name="name">requisition.purchase.match.tree</field>
+ <field name="model">requisition.purchase.match</field>
+ <field name="arch" type="xml">
+ <tree>
+ <field name="order_id" readonly="1"/>
+ <field name="vendor" readonly="1"/>
+ <field name="total" readonly="1"/>
+ </tree>
+ </field>
+ </record>
+
+ <record id="requisition_form" model="ir.ui.view">
+ <field name="name">requisition.form</field>
+ <field name="model">requisition</field>
+ <field name="arch" type="xml">
+ <form>
+ <header>
+ <button name="button_approve"
+ string="Approve"
+ type="object"
+ class="mr-2 oe_highlight"
+ />
+ </header>
+ <sheet string="Requisition">
+ <div class="oe_button_box" name="button_box"/>
+ <group>
+ <group>
+ <field name="number"/>
+ <field name="date_doc"/>
+ <field name="sale_order_id"/>
+ <field name="description"/>
+ <field name="notification" readonly="1"/>
+ </group>
+ <group>
+ <div>
+ <button name="generate_requisition_from_so"
+ string="Create Line from SO"
+ type="object"
+ class="mr-2 oe_highlight"
+ />
+ </div>
+ <div>
+ <button name="create_po_from_requisition"
+ string="Create PO"
+ type="object"
+ class="mr-2 oe_highlight"
+ />
+ </div>
+ </group>
+ </group>
+ <notebook>
+ <page string="Lines">
+ <field name="requisition_lines">
+ <tree editable="line">
+ <field name="product_id" required="1"/>
+ <field name="partner_id" required="1" />
+ <field name="qty_purchase" required="1" />
+ <field name="price_unit" required="1" />
+ <field name="taxes_id" readonly="1" />
+ <field name="subtotal" readonly="1" />
+ <field name="brand_id" />
+ </tree>
+ </field>
+ </page>
+ <page string="Matches">
+ <field name="requisition_match"/>
+ </page>
+ </notebook>
+ </sheet>
+ <div class="oe_chatter">
+ <field name="message_follower_ids" widget="mail_followers"/>
+ <field name="message_ids" widget="mail_thread"/>
+ </div>
+ </form>
+ </field>
+ </record>
+
+ <record id="requisition_action" model="ir.actions.act_window">
+ <field name="name">Requisition</field>
+ <field name="type">ir.actions.act_window</field>
+ <field name="res_model">requisition</field>
+ <field name="view_mode">tree,form</field>
+ </record>
+
+ <menuitem id="menu_requisition"
+ name="Requisition"
+ action="requisition_action"
+ parent="purchase.menu_procurement_management"
+ sequence="300"/>
+</odoo> \ No newline at end of file
diff --git a/fixco_custom/views/shipment_group.xml b/fixco_custom/views/shipment_group.xml
index 8a6c303..8ebcef9 100644
--- a/fixco_custom/views/shipment_group.xml
+++ b/fixco_custom/views/shipment_group.xml
@@ -15,7 +15,26 @@
<field name="model">shipment.group</field>
<field name="arch" type="xml">
<form>
+ <header>
+ <button name="get_status"
+ string="Get Status"
+ type="object"
+ class="oe_highlight"
+ />
+ <button name="sync_product_to_picking_line"
+ string="Sync Picking Line"
+ type="object"
+ class="oe_highlight"
+ />
+ </header>
<sheet>
+ <div class="oe_button_box" name="button_box">
+ <button type="object" name="action_view_related_line"
+ class="oe_stat_button"
+ icon="fa-pencil-square-o">
+ <field name="related_count" widget="statinfo" string="Related Line"/>
+ </button>
+ </div>
<group>
<group>
<field name="number" readonly="1"/>
@@ -25,9 +44,12 @@
<page string="Picking Line">
<field name="picking_lines">
<tree editable="bottom">
- <field name="scan_invoice_marketplace"/>
+ <field name="scan_receipt"/>
<field name="picking_id"/>
<field name="carrier"/>
+ <field name="invoice_marketplace"/>
+ <field name="order_reference" optional="hide"/>
+ <field name="status"/>
</tree>
</field>
</page>
@@ -37,6 +59,7 @@
<field name="product_id"/>
<field name="carrier"/>
<field name="invoice_marketplace"/>
+ <field name="order_reference" optional="hide"/>
<field name="picking_id"/>
</tree>
</field>
diff --git a/fixco_custom/views/shipment_line.xml b/fixco_custom/views/shipment_line.xml
new file mode 100644
index 0000000..dbeb1bb
--- /dev/null
+++ b/fixco_custom/views/shipment_line.xml
@@ -0,0 +1,109 @@
+<?xml version="1.0" encoding="utf-8"?>
+<odoo>
+ <data>
+ <record id="view_picking_line_search" model="ir.ui.view">
+ <field name="name">picking.line.search</field>
+ <field name="model">picking.line</field>
+ <field name="arch" type="xml">
+ <search>
+ <field name="carrier"/>
+ <filter string="Carrier" name="carrier_filter" domain="[('carrier','!=',False)]"/>
+ <filter string="Group by Carrier" name="group_carrier" context="{'group_by':'carrier'}"/>
+ </search>
+ </field>
+ </record>
+
+ <record id="view_product_shipment_line_search" model="ir.ui.view">
+ <field name="name">product.shipment.line.search</field>
+ <field name="model">product.shipment.line</field>
+ <field name="arch" type="xml">
+ <search>
+ <field name="carrier"/>
+ <filter string="Carrier" name="carrier_filter" domain="[('carrier','!=',False)]"/>
+ <filter string="Group by Carrier" name="group_carrier" context="{'group_by':'carrier'}"/>
+ </search>
+ </field>
+ </record>
+
+ <record id="view_picking_line_tree" model="ir.ui.view">
+ <field name="name">picking.line.tree</field>
+ <field name="model">picking.line</field>
+ <field name="arch" type="xml">
+ <tree>
+ <field name="shipment_id"/>
+ <field name="picking_id" widget="many2one_clickable"/>
+ <field name="scan_receipt"/>
+ <field name="invoice_marketplace"/>
+ <field name="carrier"/>
+ <field name="order_reference"/>
+ <field name="status"/>
+ </tree>
+ </field>
+ </record>
+
+ <record id="view_picking_line_form" model="ir.ui.view">
+ <field name="name">picking.line.form</field>
+ <field name="model">picking.line</field>
+ <field name="arch" type="xml">
+ <form>
+ <sheet>
+ <group>
+ <group>
+ <field name="shipment_id"/>
+ <field name="picking_id" widget="many2one_clickable"/>
+ <field name="scan_receipt"/>
+ </group>
+ <group>
+ <field name="invoice_marketplace"/>
+ <field name="carrier"/>
+ <field name="order_reference"/>
+ <field name="status"/>
+ </group>
+ </group>
+ <field name="product_shipment_lines" mode="tree">
+ <tree editable="bottom">
+ <field name="product_id"/>
+ <field name="carrier"/>
+ <field name="invoice_marketplace"/>
+ <field name="picking_id" widget="many2one_clickable"/>
+ <field name="order_reference"/>
+ </tree>
+ </field>
+ </sheet>
+ </form>
+ </field>
+ </record>
+
+ <record id="action_picking_line" model="ir.actions.act_window">
+ <field name="name">Shipment Lines</field>
+ <field name="res_model">picking.line</field>
+ <field name="view_mode">tree,form</field>
+ <field name="help" type="html">
+ <p class="o_view_nocontent_smiling_face">Create a new shipment line</p>
+ </field>
+ </record>
+
+ <menuitem
+ action="action_picking_line"
+ id="shipment_lines"
+ parent="stock.menu_stock_warehouse_mgmt"
+ name="Shipment Lines"
+ sequence="1"
+ />
+
+ <record id="view_product_shipment_line_tree" model="ir.ui.view">
+ <field name="name">product.shipment.line.tree</field>
+ <field name="model">product.shipment.line</field>
+ <field name="arch" type="xml">
+ <tree>
+ <field name="picking_line_id"/>
+ <field name="product_id"/>
+ <field name="carrier"/>
+ <field name="invoice_marketplace"/>
+ <field name="picking_id" widget="many2one_clickable"/>
+ <field name="order_reference"/>
+ </tree>
+ </field>
+ </record>
+ </data>
+</odoo> \ No newline at end of file