diff --git a/intercompany_transaction_ept/README.rst b/intercompany_transaction_ept/README.rst new file mode 100644 index 0000000..39a98d0 --- /dev/null +++ b/intercompany_transaction_ept/README.rst @@ -0,0 +1,89 @@ +============================================= +Inter Company Transfer and Warehouse Transfer +============================================= +Module to manage Inter Company Transfer and Inter Warehouse Transfer along with all required documents with easiest way by just simple configurations. + +======== +Features +======== +* **Common Features** +* An easy interface to transfer products from one warehouse to another warehouse. +* System will decide what kind of documents needs to be created based on companies of source warehouse and destination warehouses. +* All documents created by just one click based on the configuration of the system. + +* **Inter Warehouse Transfer (Intra company transfer)** +* Creates Delivery Order in source warehouse & Receipts in Destination warehouse. +* Smart buttons to view created Delivery Order & Receipt. +* Allows users to do reverse transfer from the same screen. It will reverse the delivery order & receipt. + +* **Inter Company Transfer** +* Creates Sales Order from source warehouse & Purchase Order in destination warehouse if warehouses belongs to different companies. +* Control over Invoice numbering / Refund numbering by specifying separate journals company wise. +* System will allow you to control pricing from the same screen. Same price will apply on sales order of source company and purchase order of destination company. +* An option to reverse entire / partial transactions with all effects. In case of Reverse intercompany transactions system will create reverse of delivery order, reverse of incoming shipment (only if they are done), debit note and credit note. +* Control over all documents process like automatic workflow of all documents, whether need to create or not, auto confirm or not, auto validate or not. Like one can control Auto confirm Sales Orders/ Purchase Order or Generate & Validate Invoice and Cancel: create credit note and reconcile. +* In multi company environment it’s not easy to create documents of multi companies without login to that company, with this app user can create all records related to intercompany transactions by just one click without login to that company, for that just need to set intercompany user company wise. So you can be sure that taxes applied on SO & PO are accurate according to the companies. + +==================================== +Recommended ICT Configurations +==================================== + +* **COMMON CONFIGURATIONS** +* Company Specific + Set Inter Company user + Inter company users must have rights same as an admin (to avoid access rights issue during process) but can have an access to only single company in which you set it. + Set customer invoice and vendor bill journal (Optional) + +* Warehouses Specific + Set resupply warehouses in warehouse to generate automatic routes for internal transfers +* Product Specific + If products are common for all companies then remove / do not set company in products (if products needs to be shared by all companies). + Vendor price configurations. + Set routes in product + +* Partners Specific + Set customer location & Vendor location in partner + Access Right: Inter company users must have an access of following areas. + Sales Manager + Purchase Manager + Inventory Manager + Accounting Manager + Multi Warehouses + ICT Manager + +* ICT Configurations (Automatic workflow) + Auto confirm sale / purchase order + Auto create invoice + Auto validate invoice +* Reverse ICT + Create a draft credit note + Cancel: create credit note and reconcile + +* **COMPANY WISE CONFIGURATIONS** +(Company wise configurations means user need to login to each company and need to do configurations. Value of the fields might different company by company, field is unique but value might different for each company.) + +* Product + Customer taxes and vendor taxes +* Partners + Set customer price list for partner for each companies + Set fiscal position for partner for each companies +============ +Similar Apps +============ +Inter Company Transfer +ICT +Internal Transfer +Warehouse transfer +warehouse to warehouse transfer +Intercompany transfer +company to company transfer +transfer between warehouses +transfer between companies +the inter company transfer with odoo +odoo intercompany +inter company transaction in odoo +warehouse transfer odoo +odoo inter warehouse transfer +odoo internal company transfer +odoo inter company transfer +odoo intra company transfer diff --git a/intercompany_transaction_ept/__init__.py b/intercompany_transaction_ept/__init__.py new file mode 100644 index 0000000..fa732aa --- /dev/null +++ b/intercompany_transaction_ept/__init__.py @@ -0,0 +1,33 @@ +from . import models +from . import wizard +from . import report +from odoo.api import Environment, SUPERUSER_ID +import logging + +_logger=logging.getLogger(__name__) + + + +multi_company_ir_rules = {'stock.stock_warehouse_comp_rule':'stock.group_stock_user', + 'stock.stock_location_comp_rule':'stock.group_stock_user', + 'stock.stock_picking_type_rule':'stock.group_stock_user'} + + + +def uninstall_hook_update_rule(cr, registry): + env = Environment(cr, SUPERUSER_ID, {}) + for rule_xml_id,group_xml_id in multi_company_ir_rules.items() : + rule = env.ref(rule_xml_id) + group = env.ref(group_xml_id) + if group in rule.groups : + rule.write({'groups':[(3,group.id)]}) + + +def post_init_update_rule(cr,registry): + env = Environment(cr, SUPERUSER_ID, {}) + for rule_xml_id,group_xml_id in multi_company_ir_rules.items() : + rule = env.ref(rule_xml_id) + group = env.ref(group_xml_id) + if rule and group : + if group not in rule.groups : + rule.write({'groups':[(4,group.id)]}) \ No newline at end of file diff --git a/intercompany_transaction_ept/__manifest__.py b/intercompany_transaction_ept/__manifest__.py new file mode 100644 index 0000000..4f273a6 --- /dev/null +++ b/intercompany_transaction_ept/__manifest__.py @@ -0,0 +1,50 @@ +{ + + # App information + 'name': 'Inter Company Transfer and Warehouse Transfer', + 'version': '12.0', + 'category': 'stock', + 'license': 'OPL-1', + 'summary' : 'Module to manage Inter Company Transfer and Inter Warehouse Transfer along with all required documents with easiest way by just simple configurations.', + + + # Author + + 'author': 'Emipro Technologies Pvt. Ltd.', + 'website': 'http://www.emiprotechnologies.com/', + 'maintainer': 'Emipro Technologies Pvt. Ltd.', + + # Dependencies + + 'depends': ['delivery', 'sale', 'purchase', 'stock', 'barcodes'], + 'data': [ + 'data/ir_sequence.xml', + 'data/intercompany_transaction_config.xml', + 'views/inter_company_transfer_views.xml', + 'views/inter_company_transfer_config_views.xml', + 'views/rescompany_views.xml', + 'views/sale_views.xml', + 'views/purchase_views.xml', + 'views/stock_picking_views.xml', + 'views/all_search_views.xml', + 'wizard/reverse_inter_company_transfer_views.xml', + 'wizard/import_export_product_list_views.xml', + 'security/inter_company_transfer_security.xml', + 'security/ir.model.access.csv', + 'views/inter_company_transfer_process_log_views.xml' + ], + + # Odoo Store Specific + 'images': ['static/description/Inter-Company-Transfer-cover.png'], + + # Technical + 'post_init_hook': 'post_init_update_rule', + 'uninstall_hook': 'uninstall_hook_update_rule', + 'live_test_url': 'https://www.emiprotechnologies.com/free-trial?app=intercompany-transaction-ept&version=12&edition=enterprise', + 'active': True, + 'installable': True, + 'currency': 'EUR', + 'price': 149.00, + 'auto_install': False, + 'application': True, +} diff --git a/intercompany_transaction_ept/__pycache__/__init__.cpython-36.pyc b/intercompany_transaction_ept/__pycache__/__init__.cpython-36.pyc new file mode 100644 index 0000000..be76e43 Binary files /dev/null and b/intercompany_transaction_ept/__pycache__/__init__.cpython-36.pyc differ diff --git a/intercompany_transaction_ept/data/intercompany_transaction_config.xml b/intercompany_transaction_ept/data/intercompany_transaction_config.xml new file mode 100644 index 0000000..87f6653 --- /dev/null +++ b/intercompany_transaction_ept/data/intercompany_transaction_config.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/intercompany_transaction_ept/data/ir_sequence.xml b/intercompany_transaction_ept/data/ir_sequence.xml new file mode 100644 index 0000000..ca75d58 --- /dev/null +++ b/intercompany_transaction_ept/data/ir_sequence.xml @@ -0,0 +1,39 @@ + + + + + Intercompany Transfer + ICT + 3 + 1 + 1 + + + + + Reverse Intercompany Transaction + RICT + 3 + 1 + 1 + + + + + Internal Transfer + INTICT + 3 + 1 + 1 + + + + + Inter Company Transfer LOG + ict_log + 4 + LOG + + + + \ No newline at end of file diff --git a/intercompany_transaction_ept/models/__init__.py b/intercompany_transaction_ept/models/__init__.py new file mode 100644 index 0000000..3ffbfc7 --- /dev/null +++ b/intercompany_transaction_ept/models/__init__.py @@ -0,0 +1,9 @@ +from . import inter_company_transfer_ept +from . import inter_company_transfer_line_ept +from . import inter_company_transfer_config_ept +from . import res_company +from . import sale +from . import purchase +from . import stock_picking +from . import account_invoice +from . import inter_company_transfer_log_process_ept \ No newline at end of file diff --git a/intercompany_transaction_ept/models/__pycache__/__init__.cpython-36.pyc b/intercompany_transaction_ept/models/__pycache__/__init__.cpython-36.pyc new file mode 100644 index 0000000..2db8a59 Binary files /dev/null and b/intercompany_transaction_ept/models/__pycache__/__init__.cpython-36.pyc differ diff --git a/intercompany_transaction_ept/models/__pycache__/account_invoice.cpython-36.pyc b/intercompany_transaction_ept/models/__pycache__/account_invoice.cpython-36.pyc new file mode 100644 index 0000000..cc65f2f Binary files /dev/null and b/intercompany_transaction_ept/models/__pycache__/account_invoice.cpython-36.pyc differ diff --git a/intercompany_transaction_ept/models/__pycache__/inter_company_transfer_config_ept.cpython-36.pyc b/intercompany_transaction_ept/models/__pycache__/inter_company_transfer_config_ept.cpython-36.pyc new file mode 100644 index 0000000..7fd61dc Binary files /dev/null and b/intercompany_transaction_ept/models/__pycache__/inter_company_transfer_config_ept.cpython-36.pyc differ diff --git a/intercompany_transaction_ept/models/__pycache__/inter_company_transfer_ept.cpython-36.pyc b/intercompany_transaction_ept/models/__pycache__/inter_company_transfer_ept.cpython-36.pyc new file mode 100644 index 0000000..4760c5c Binary files /dev/null and b/intercompany_transaction_ept/models/__pycache__/inter_company_transfer_ept.cpython-36.pyc differ diff --git a/intercompany_transaction_ept/models/__pycache__/inter_company_transfer_line_ept.cpython-36.pyc b/intercompany_transaction_ept/models/__pycache__/inter_company_transfer_line_ept.cpython-36.pyc new file mode 100644 index 0000000..35aae6b Binary files /dev/null and b/intercompany_transaction_ept/models/__pycache__/inter_company_transfer_line_ept.cpython-36.pyc differ diff --git a/intercompany_transaction_ept/models/__pycache__/inter_company_transfer_log_process_ept.cpython-36.pyc b/intercompany_transaction_ept/models/__pycache__/inter_company_transfer_log_process_ept.cpython-36.pyc new file mode 100644 index 0000000..5e9b1de Binary files /dev/null and b/intercompany_transaction_ept/models/__pycache__/inter_company_transfer_log_process_ept.cpython-36.pyc differ diff --git a/intercompany_transaction_ept/models/__pycache__/purchase.cpython-36.pyc b/intercompany_transaction_ept/models/__pycache__/purchase.cpython-36.pyc new file mode 100644 index 0000000..ce376cb Binary files /dev/null and b/intercompany_transaction_ept/models/__pycache__/purchase.cpython-36.pyc differ diff --git a/intercompany_transaction_ept/models/__pycache__/res_company.cpython-36.pyc b/intercompany_transaction_ept/models/__pycache__/res_company.cpython-36.pyc new file mode 100644 index 0000000..44ea374 Binary files /dev/null and b/intercompany_transaction_ept/models/__pycache__/res_company.cpython-36.pyc differ diff --git a/intercompany_transaction_ept/models/__pycache__/sale.cpython-36.pyc b/intercompany_transaction_ept/models/__pycache__/sale.cpython-36.pyc new file mode 100644 index 0000000..7030aee Binary files /dev/null and b/intercompany_transaction_ept/models/__pycache__/sale.cpython-36.pyc differ diff --git a/intercompany_transaction_ept/models/__pycache__/stock_picking.cpython-36.pyc b/intercompany_transaction_ept/models/__pycache__/stock_picking.cpython-36.pyc new file mode 100644 index 0000000..654e64c Binary files /dev/null and b/intercompany_transaction_ept/models/__pycache__/stock_picking.cpython-36.pyc differ diff --git a/intercompany_transaction_ept/models/account_invoice.py b/intercompany_transaction_ept/models/account_invoice.py new file mode 100644 index 0000000..76b3885 --- /dev/null +++ b/intercompany_transaction_ept/models/account_invoice.py @@ -0,0 +1,19 @@ +from odoo import api, models, fields , _ + +class AccountInvoice(models.Model): + + _inherit = 'account.move' + _description = 'Account Invoice' + + + intercompany_transfer_id = fields.Many2one('inter.company.transfer.ept', string="ICT", copy=False) + + @api.model + def create(self, vals): + res = super(AccountInvoice, self).create(vals) + order_id = self.env['sale.order'].search([('name', '=', res.origin)]) + if not order_id: + order_id = self.env['purchase.order'].search([('name', '=', res.origin)]) + if order_id and order_id.intercompany_transfer_id: + res.intercompany_transfer_id = order_id.intercompany_transfer_id.id + return res diff --git a/intercompany_transaction_ept/models/inter_company_transfer_config_ept.py b/intercompany_transaction_ept/models/inter_company_transfer_config_ept.py new file mode 100644 index 0000000..bebdf9b --- /dev/null +++ b/intercompany_transaction_ept/models/inter_company_transfer_config_ept.py @@ -0,0 +1,28 @@ +from odoo import models, fields, api, _ +from odoo.exceptions import Warning + +class InterCompanyTransferConfig(models.Model): + _name = "inter.company.transfer.config.ept" + _rec_name = "sequence_id" + _description = 'Inter Company Transfer Config' + + @api.model + def _default_company(self): + return self.env.user.company_id.id + + description = fields.Char("Discription") + + filter_refund = fields.Selection([('refund', 'Create a draft credit note'), ('cancel', 'Cancel: create credit note and reconcile')], default='refund', string='Refund Method', help='Refund base on this type. You can not Modify and Cancel if the invoice is already reconciled') + + auto_confirm_orders = fields.Boolean('Auto Confirm Orders') + auto_create_invoices = fields.Boolean('Auto Create Invoices') + auto_validate_invoices = fields.Boolean('Auto Validate Invoices') + + sequence_id = fields.Many2one('ir.sequence', 'Sequence') + company_id = fields.Many2one('res.company', string='Company', readonly=True, compute=_default_company, store=True) + + + + + def unlink(self): + raise Warning(_("You can not delete this record")) diff --git a/intercompany_transaction_ept/models/inter_company_transfer_ept.py b/intercompany_transaction_ept/models/inter_company_transfer_ept.py new file mode 100644 index 0000000..04ddfb1 --- /dev/null +++ b/intercompany_transaction_ept/models/inter_company_transfer_ept.py @@ -0,0 +1,740 @@ +from odoo import models, fields, api, _ +from odoo.exceptions import UserError, ValidationError, Warning +from datetime import datetime +from lxml import etree + + +class InterCompanyTransfer(models.Model): + + _name = 'inter.company.transfer.ept' + _description = "Internal Company Transfer" + _inherit = ['mail.thread', 'mail.activity.mixin', 'barcodes.barcode_events_mixin'] + _order = 'id desc' + + def on_barcode_scanned(self, barcode): + product_obj = self.env['product.product'] + intercompany_transferline_obj = self.env['inter.company.transfer.line.ept'] + + product_id = product_obj.search(['|', ('barcode', '=', barcode), ('default_code', '=', barcode)], limit=1) + if not product_id: + return {'warning': { + 'title': _('Warning'), + 'message': _('Product Not Found') + }, + } + current_id = self._origin + line = intercompany_transferline_obj.search([('inter_transfer_id', '=', current_id.id), ('product_id', '=', product_id.id)], limit=1) + if line: + line.write({'quantity':line.quantity + 1}) + else: + intercompany_transferline_obj.create({'inter_transfer_id':current_id.id, + 'product_id':product_id.id, + 'quantity':1}) + + + + def _get_default_team(self): + return self.env['crm.team']._get_default_team_id() + + @api.depends('log_ids') + def _compute_log_ids(self): + for ict in self: + ict.log_count = len(ict.log_ids) + + name = fields.Char('Name') + message = fields.Char("Message", copy=False) + + state = fields.Selection([('draft', 'Draft'), ('processed', 'Processed'), ('cancel', 'Cancelled')], string='State', copy=False, default='draft') + type = fields.Selection([('ict', 'ICT'), ('ict_reverse', 'Reverce ICT'), ('internal', 'Internal')], string='Type', readonly=True, copy=False, default='ict') + log_count = fields.Integer(string='Inter Company Log Count', compute='_compute_log_ids') + + processed_date = fields.Datetime("Processed Date", copy=False) + + source_warehouse_id = fields.Many2one('stock.warehouse', string='From Warehouse') + source_company_id = fields.Many2one(related='source_warehouse_id.company_id', string="Source Company") + destination_warehouse_id = fields.Many2one('stock.warehouse', string='To Warehouse') + destination_company_id = fields.Many2one(related='destination_warehouse_id.company_id', string="Destination Company") + + crm_team_id = fields.Many2one('crm.team', string="Sales Team", default=_get_default_team) + price_list_id = fields.Many2one('product.pricelist', string="Price List") + currency_id = fields.Many2one('res.currency', related="price_list_id.currency_id", string="Currency") + incoming_shipment_id = fields.Many2one('stock.picking', string="Incoming Shipment", copy=False) + group_id = fields.Many2one('procurement.group', string="Procurement Group", copy=False) + + intercompany_transfer_id = fields.Many2one('inter.company.transfer.ept', string="ICT") + reverse_intercompanytransfer_ids = fields.One2many('inter.company.transfer.ept', 'intercompany_transfer_id', string="Reverse ICT") + + log_ids = fields.One2many('inter.company.transfer.log.ept', 'intercompany_transfer_id', string="Inter Company Log") + + saleorder_ids = fields.One2many('sale.order', 'intercompany_transfer_id', sring='Sale Orders', copy=False) + purchaseorder_ids = fields.One2many('purchase.order', 'intercompany_transfer_id', string="Purchase Order", copy=False) + invoice_ids = fields.One2many('account.move', 'intercompany_transfer_id', String="Invoices", copy=False) + picking_ids = fields.One2many('stock.picking', 'intercompany_transfer_id', string="Pickings", copy=False) + intercompany_transferline_ids = fields.One2many('inter.company.transfer.line.ept', 'inter_transfer_id', string="Transfer Lines", copy=True) + + _sql_constraints = [('src_dest_company_uniq', 'CHECK(source_warehouse_id!=destination_warehouse_id)', 'Source Warehouse and Destination warehouse must be different!')] + + + + @api.model + def create(self, vals): + res = super(InterCompanyTransfer, self).create(vals) + record_name = "NEW" + if res.type == 'ict' or not res.type: + sequence_id = self.env.ref('intercompany_transaction_ept.ir_sequence_intercompany_transaction').ids + if sequence_id: + record_name = self.env['ir.sequence'].browse(sequence_id).next_by_id() + res.update({'name':record_name}) + elif res.type == 'ict_reverse': + sequence_id = self.env.ref('intercompany_transaction_ept.ir_sequence_reverse_intercompany_transaction').ids + if sequence_id: + record_name = self.env['ir.sequence'].browse(sequence_id).next_by_id() + res.update({'name':record_name}) + elif res.type == 'internal': + sequence_id = self.env.ref('intercompany_transaction_ept.ir_sequence_internal_transfer_intercompany_transaction').ids + if sequence_id: + record_name = self.env['ir.sequence'].browse(sequence_id).next_by_id() + res.update({'name':record_name}) + return res + + + + @api.model + def fields_view_get(self, view_id=None, view_type='form', toolbar=False, submenu=False): + context = self._context + res = super(InterCompanyTransfer, self).fields_view_get(view_id=view_id, view_type=view_type, toolbar=toolbar, submenu=submenu) + doc = etree.XML(res['arch']) + if view_type in ['form', 'tree']: + if context.get('type', 'ict_reverse') == 'ict_reverse': + for node in doc.xpath("//tree[@string='Inter Company Transfer']"): + node.set('create', 'false') + for node in doc.xpath("//form[@string='Inter Company Transfer']"): + node.set('create', 'false') + res['arch'] = etree.tostring(doc) + return res + + @api.onchange('source_warehouse_id') + def source_warehouse_id_onchange(self): + if not self.source_warehouse_id: + self.destination_warehouse_id = False + return + if self.source_warehouse_id == self.destination_warehouse_id: + self.destination_warehouse_id = False + self.currency_id = self.source_company_id.currency_id + + res = {} + if self.type == 'internal': + domain = {'destination_warehouse_id': [('company_id', '=', self.source_company_id.id), ('id', '!=', self.source_warehouse_id.id)]} + return {'domain': domain} + elif self.type != 'internal': + domain = {'destination_warehouse_id': [('company_id', '!=', self.source_company_id.id)]} + return {'domain': domain} + return res + + + @api.onchange('destination_warehouse_id') + def onchange_destination_warehouse_id(self): + if not self.destination_warehouse_id: + return False + + if not self.source_company_id.sudo().intercompany_user_id: + msg = 'Please Specify Inter Company User for Source Company' + raise Warning(msg) + + if not self.destination_company_id.sudo().intercompany_user_id: + msg = 'Please Specify Inter Company User for Destination Company' + raise Warning(msg) + + self.price_list_id = self.destination_company_id.sudo().partner_id.sudo(self.source_company_id.intercompany_user_id.id).property_product_pricelist + self.crm_team_id = self.destination_company_id.sudo().partner_id.sudo(self.source_company_id.intercompany_user_id.id).team_id + return + + + @api.onchange('price_list_id') + def default_price(self): + for record in self: + for line in record.reverse_intercompanytransfer_ids: + line.default_price_get() + return + + def action_process(self): + invoice_obj = self.env['account.move'] + stock_immediate_transfer_obj = self.env['stock.immediate.transfer'] + sale_advance_paymentobj = self.env['sale.advance.payment.inv'] + + context = self._context.copy() or {} + for record in self: + sale_journal = record.source_company_id.sale_journal + purchase_journal = record.destination_company_id.purchase_journal + if not record.with_context(context).check_user_validation(): + continue + + + if not record.intercompany_transferline_ids: + msg = "Please Add the Product to Process Transaction" + raise ValidationError(msg) + + sale_user_id = record.sudo().source_company_id.intercompany_user_id.id + purchase_user_id = record.sudo().destination_company_id.intercompany_user_id.id + purchase_partner_id = record.source_company_id.sudo().partner_id + + configuration_record = record.env.ref('intercompany_transaction_ept.intercompany_transaction_config_record') + + if record.source_company_id == record.destination_company_id: + is_create_transfer = self.create_internal_transfer() + if is_create_transfer: + record.write({'state':'processed', 'processed_date':datetime.today(), 'message':'ICT processed successfully by %s' % (self.env.user.name)}) + return + + sale_orders = record.auto_create_saleorder() + purchase_orders = record.auto_create_purchaseorder() + + if configuration_record: + if configuration_record.auto_confirm_orders: + for order in sale_orders: + order.write({'origin':record.name or ''}) + order.sudo(sale_user_id).action_confirm() + + for order in purchase_orders: + order.write({'origin':record.name or ''}) + order.sudo(purchase_user_id).button_confirm() + + + + if configuration_record.auto_create_invoices: + invoice_id = False + for order in sale_orders: + context = {"active_model": 'sale.order', "active_ids": [order.id], "active_id": order.id, 'open_invoices':True} + if sale_journal: + context.update({'default_journal_id':sale_journal.id}) + payment_id = sale_advance_paymentobj.sudo(sale_user_id).create({'advance_payment_method': 'delivered'}) + result = payment_id.with_context(context).sudo(sale_user_id).create_invoices() + result = result.get('res_id', False) + invoice_id = invoice_obj.sudo(sale_user_id).browse(result) + invoice_id.sudo(sale_user_id).write({'date_invoice':str(datetime.today()), 'intercompany_transfer_id':self.id}) + + + vendor_bill_id = False + for porder in purchase_orders: + context = {'default_type': 'in_invoice', 'type': 'in_invoice', 'journal_type': 'purchase', 'default_purchase_id': porder.id} + if purchase_journal: + context.update({'default_journal_id':record.destination_company_id.purchase_journal.id}) + + invoice_dict = self.prepare_invoice_dict(record, purchase_partner_id, porder) + invoice_vals = invoice_obj.sudo(purchase_user_id).with_context(context).new(invoice_dict) + invoice_vals.purchase_id = porder.id + invoice_vals.journal_id = invoice_vals.sudo(purchase_user_id)._default_journal() + invoice_vals.sudo(purchase_user_id).purchase_order_change() + invoice_vals.sudo(purchase_user_id)._onchange_partner_id() + invoice_vals.date_invoice = str(datetime.today()) + invoice_vals.sudo(purchase_user_id)._onchange_payment_term_date_invoice() + invoice_vals.sudo(purchase_user_id)._onchange_origin() + invoice_vals.currency_id = record.currency_id + + for line in invoice_vals.invoice_line_ids: + line.quantity = line.purchase_line_id and line.purchase_line_id.product_qty or 0.0 + line.sudo(purchase_user_id)._compute_price() + + vendor_bill_id = invoice_obj.sudo(purchase_user_id).with_context({'type':'in_invoice'}).create(invoice_vals._convert_to_write(invoice_vals._cache)) + vendor_bill_id.intercompany_transfer_id = self.id + + if configuration_record.auto_validate_invoices: + invoice_id.sudo(sale_user_id).action_invoice_open() + vendor_bill_id.sudo(purchase_user_id).action_invoice_open() + + record.write({'state':'processed', 'processed_date':datetime.today(), 'message':'ICT processed successfully by %s' % (self.env.user.name)}) + + return True + + + def check_user_validation(self): + context = self._context or {} + for record in self: + if not record.source_warehouse_id.sudo().company_id.intercompany_user_id: + msg = 'Please Specify Inter Company user for Source Company' + if context.get('is_auto_validate', False): + record.write({'message':msg}) + return False + raise ValidationError(msg) + + if not record.destination_warehouse_id.sudo().company_id.intercompany_user_id: + msg = 'Please specify intercompany user for destination company' + if context.get('is_auto_validate', False): + record.write({'message':msg}) + return False + raise ValidationError(msg) + + if record.source_warehouse_id.sudo().company_id not in self.env.user.company_ids : + if record.source_warehouse_id.sudo().company_id not in self.env.user.company_id.child_ids : + msg = "User '%s' can not process this Inter Company Transfer.\n User from Source Warehouse Company can Process it !!!!\n\nPlease Process it with User of Source Warehouse Company." % (self.env.user.name) + raise ValidationError(msg) + return True + + + def create_internal_transfer(self): + picking_obj = self.env['stock.picking'] + procurementgroup_obj = self.env['procurement.group'] + stocklocation_route_obj = self.env['stock.location.route'] + + source_wh = self.source_warehouse_id + dest_wh = self.destination_warehouse_id + + group_id = procurementgroup_obj.create({'name': self.name, 'partner_id': dest_wh.partner_id.id}) + self.group_id = group_id.id + route_ids = stocklocation_route_obj.search([('supplied_wh_id', '=', dest_wh.id), ('supplier_wh_id', '=', source_wh.id)]) + if not route_ids: + raise ValidationError(_("No routes are found. \n Please configure warehouse routes and set in products.")) + if not self.intercompany_transferline_ids : + raise ValidationError(_("No Products found. \n Please add products to transfer.")) + + for line in self.intercompany_transferline_ids: + procurementgroup_obj.run(line.product_id, line.quantity, line.product_id.uom_id, dest_wh.lot_stock_id, self.name, False, values={'warehouse_id':dest_wh, 'route_ids':route_ids and route_ids[0], 'group_id':self.group_id}) + + + pickings = picking_obj.search([('group_id', '=', group_id.id)]) + if not pickings: + if not group_id: + raise Warning("Problem with creation of procurement group.") + else: + raise Warning("NO Pickings are created for this record.") + for picking in pickings: + if not picking.intercompany_transfer_id: + picking.intercompany_transfer_id = self.id + picking.action_assign() + picking_id = picking.search([('location_id', '=', self.source_warehouse_id.lot_stock_id.id)]) + if picking_id: + picking_id.action_assign() + + return True + + def auto_create_saleorder(self): + sale_obj = self.env['sale.order'] + saleline_obj = self.env['sale.order.line'] + so_list = [] + + for record in self: + source_company = record.source_company_id + source_warehouse_id = record.source_warehouse_id + intercompany_user = source_company.sudo().intercompany_user_id.id or False + partner_id = record.destination_company_id.sudo().partner_id + order_vals = sale_obj.sudo(intercompany_user).new({'partner_id':partner_id.id, 'warehouse_id':source_warehouse_id.id, 'pricelist_id':self.price_list_id.id}) + order_vals.sudo(intercompany_user).onchange_partner_id() + order_vals.warehouse_id = source_warehouse_id.id + order_vals.sudo(intercompany_user)._onchange_company_id() + order_vals.fiscal_position_id = partner_id.sudo(intercompany_user).property_account_position_id.id + order_vals.pricelist_id = self.price_list_id.id + if record.crm_team_id: + order_vals.team_id = record.crm_team_id.id + order_vals = order_vals.sudo(intercompany_user) + sale_order = sale_obj.sudo(intercompany_user).create(order_vals._convert_to_write(order_vals._cache)) + so_lines_list = [] + for line in record.intercompany_transferline_ids: + line_vals = saleline_obj.sudo(intercompany_user).new({'order_id':sale_order.id, 'product_id':line.product_id}) + line_vals.sudo(intercompany_user).product_id_change() + line_vals.sudo(intercompany_user).product_uom_qty = line.quantity + line_vals.price_unit = line.price + line_vals = line_vals.sudo(intercompany_user)._convert_to_write(line_vals._cache) + so_lines_list.append((0, 0, line_vals)) + sale_order.sudo(intercompany_user).write({'order_line':so_lines_list, 'intercompany_transfer_id':record.id}) + so_list.append(sale_order) + + return so_list + + + def auto_create_purchaseorder(self): + purchase_obj = self.env['purchase.order'] + purchase_line_obj = self.env['purchase.order.line'] + po_list = [] + for record in self: + destination_company = record.destination_company_id + intercompany_user = destination_company.sudo().intercompany_user_id.id or False + print('intercompany_user$$$$$$$$$$$$$$$$$$$$$$',intercompany_user) + order_vals = purchase_obj.sudo(intercompany_user).new({'currency_id':self.currency_id.id, 'partner_id':record.source_warehouse_id.sudo().company_id.partner_id.id, 'company_id':destination_company.id}) + order_vals.sudo(intercompany_user).onchange_partner_id() + order_vals.currency_id = self.currency_id.id + order_vals.picking_type_id = self.destination_warehouse_id.sudo().in_type_id + purchase_order_id = purchase_obj.sudo(intercompany_user).create(order_vals.sudo(intercompany_user)._convert_to_write(order_vals._cache)) + po_lines_list = [] + for line in record.intercompany_transferline_ids: + line_vals = purchase_line_obj.sudo(intercompany_user).new({'order_id':purchase_order_id.id, 'product_id':line.product_id, 'currency_id':self.currency_id}) + line_vals.sudo(intercompany_user).onchange_product_id() + line_vals.product_qty = line.quantity + line_vals.price_unit = line.price + line_vals.product_uom = line.product_id.uom_id + line_vals = line_vals.sudo(intercompany_user)._convert_to_write(line_vals._cache) + po_lines_list.append((0, 0, line_vals)) + purchase_order_id.sudo(intercompany_user).write({'order_line':po_lines_list, 'intercompany_transfer_id':record.id}) + po_list.append(purchase_order_id) + + return po_list + + + def prepare_invoice_dict(self, record, purchase_partner_id, porder): + vals = {'company_id': record.destination_company_id.id or False, + 'currency_id':record.currency_id, + 'partner_id':purchase_partner_id.id, + 'type': 'in_invoice', + 'journal_type': 'purchase', + 'purchase_id': porder.id} + return vals + + + def action_reverse_process(self): + stockreturn_picking_obj = self.env['stock.return.picking'] + accountinvoice_refund_obj = self.env['account.invoice.refund'] + stock_move_obj = self.env['stock.move'] + stock_picking_obj = self.env['stock.picking'] + account_invoice_obj = self.env['account.move'] + + + picking_to_stock = [] + pickings = [] + internal_transfer = False + if not self.intercompany_transferline_ids: + raise Warning("There are no products in the record!!") + + if not self.intercompany_transfer_id.saleorder_ids and not self.intercompany_transfer_id.purchaseorder_ids: + pickings = self.intercompany_transfer_id.picking_ids + if not pickings: + raise ValidationError(_("There are no pikings available in %s " % self.intercompany_transfer_id.name)) + if not pickings.filtered(lambda pc : pc.state == 'done'): + raise ValidationError(_("%s have some pickings which are not in done state yet!! \n Please done pickings befor reverse it. " % self.intercompany_transfer_id.name)) + internal_transfer = True + + if internal_transfer: + processed = False + for picking in pickings: + picking_to_stock = [] + for line in self.intercompany_transferline_ids: + for move_id in stock_move_obj.search([('picking_id', '=', picking.id), ('product_id', '=', line.product_id.id), ('state', '=', 'done')]): + line_tmp = (0, 0, {'product_id': move_id.product_id.id, 'move_id': move_id.id, 'quantity': line.quantity, 'to_refund': False}) + picking_to_stock.append(line_tmp) + + default_vals = stockreturn_picking_obj.with_context({'active_id':picking.id}).default_get(['move_dest_exists', 'original_location_id', 'parent_location_id', 'location_id', 'product_return_moves']) + default_vals.update({'product_return_moves':picking_to_stock}) + return_picking = stockreturn_picking_obj.with_context({'active_ids':[]}).create(default_vals) + new_picking_ids = return_picking.with_context({'active_id':move_id.picking_id.id}).create_returns() + stock_picking_lst = stock_picking_obj.browse(new_picking_ids.get('res_id')) + if stock_picking_lst: + for picking in stock_picking_lst: + picking.intercompany_transfer_id = self.id + processed = True + if processed: + self.write({'state':'processed'}) + return True + return False + + if self.intercompany_transfer_id.saleorder_ids: + for sorder in self.intercompany_transfer_id.saleorder_ids: + pickings += sorder.picking_ids and sorder.picking_ids.filtered(lambda picking : picking.picking_type_id.code == 'outgoing') + if not pickings: + raise ValidationError(_("No pickings are available in sale order")) + + for picking in pickings: + for line in self.intercompany_transferline_ids: + for move_id in stock_move_obj.search([('picking_id', '=', picking.id), ('product_id', '=', line.product_id.id), ('state', '=', 'done')]): + line_tmp = (0, 0, {'product_id': move_id.product_id.id, 'move_id': move_id.id, 'quantity': line.quantity, 'to_refund': False}) + picking_to_stock.append(line_tmp) + + default_vals = stockreturn_picking_obj.with_context({'active_id':picking.id}).default_get(['move_dest_exists', 'original_location_id', 'parent_location_id', 'location_id', 'product_return_moves']) + default_vals.update({'product_return_moves':picking_to_stock}) + return_picking = stockreturn_picking_obj.with_context({'active_id':picking.id}).create(default_vals) + new_picking_id = return_picking.with_context({'active_id':picking.id}).create_returns() + stock_picking_id = stock_picking_obj.browse(new_picking_id.get('res_id')) + if stock_picking_id: + stock_picking_id.intercompany_transfer_id = self.id + + + + incoming_picking_stock_lst = [] + incoming_pickings_lst = [] + if self.intercompany_transfer_id.purchaseorder_ids: + for porder in self.intercompany_transfer_id.purchaseorder_ids: + incoming_pickings_lst += porder.picking_ids and porder.picking_ids.filtered(lambda pck : pck.picking_type_id.code == 'incoming') + for incoming_picking in incoming_pickings_lst: + for line in self.intercompany_transferline_ids: + for move_id in stock_move_obj.search([('picking_id', '=', incoming_picking.id), ('product_id', '=', line.product_id.id), ('state', '=', 'done')]): + line_tmp = (0, 0, {'product_id': move_id.product_id.id, 'move_id': move_id.id, 'quantity': line.quantity}) + incoming_picking_stock_lst.append(line_tmp) + + default_incoming_vals = stockreturn_picking_obj.with_context({'active_id':incoming_picking.id}).default_get(['move_dest_exists', 'original_location_id', 'parent_location_id', 'location_id', 'product_return_moves']) + default_incoming_vals.update({'product_return_moves':incoming_picking_stock_lst}) + return_picking = stockreturn_picking_obj.with_context({'active_ids':[]}).create(default_incoming_vals) + new_picking_id = return_picking.with_context({'active_id':incoming_picking.id}).create_returns() + stock_picking = self.env['stock.picking'].browse(new_picking_id.get('res_id')) + + if stock_picking: + stock_picking.intercompany_transfer_id = self.id + + + for sorder in self.intercompany_transfer_id.saleorder_ids: + for invoice in sorder.invoice_ids.filtered(lambda inv : inv.type == 'out_invoice'): + customer_invoice_id = invoice.search([('refund_invoice_id', '=', invoice.id)], order='id desc' , limit=1) + default_inovoice_vals = accountinvoice_refund_obj.with_context({'active_id':invoice.id}).default_get(['filter_refund', 'description', 'date_invoice', 'date']) + configuration_record = self.env.ref('intercompany_transaction_ept.intercompany_transaction_config_record') + if configuration_record.filter_refund: + default_inovoice_vals['filter_refund'] = configuration_record.filter_refund + default_inovoice_vals.update({'description':'%s' % (configuration_record and configuration_record.description or ('for %s' % self.name))}) + customer_refund = accountinvoice_refund_obj.with_context({'active_id':invoice.id}).create(default_inovoice_vals) + if customer_refund.with_context({'active_ids':invoice.id}).invoice_refund(): + invoice_id = account_invoice_obj.search([('refund_invoice_id', '=', invoice.id)], order='id desc', limit=1) + if invoice_id: + invoice_id.intercompany_transfer_id = self.id + for invoice_line in invoice_id.invoice_line_ids: + match_line = self.intercompany_transferline_ids.filtered(lambda ln : ln.product_id.id == invoice_line.product_id.id) + if match_line: + invoice_line.quantity = match_line.quantity + if invoice_id.state == "draft": + invoice_id.with_context({'active_ids':invoice.id}).action_invoice_open() + + + for porder in self.intercompany_transfer_id.purchaseorder_ids: + for vendor_invoice in porder.invoice_ids.filtered(lambda inv : inv.type == 'in_invoice'): + default_inovoice_vals = accountinvoice_refund_obj.with_context({'active_id':vendor_invoice.id}).default_get(['filter_refund', 'description', 'date_invoice', 'date']) + configuration_record = self.env.ref('intercompany_transaction_ept.intercompany_transaction_config_record') + if configuration_record.filter_refund: + default_inovoice_vals['filter_refund'] = configuration_record.filter_refund + default_inovoice_vals.update({'description':'%s' % (configuration_record and configuration_record.description or ('for %s' % self.name))}) + vendor_refund = accountinvoice_refund_obj.with_context({'active_id':vendor_invoice.id}).create(default_inovoice_vals) + invoice_id = False + if vendor_refund.with_context({'active_ids':vendor_invoice.id}).invoice_refund(): + invoice_id = account_invoice_obj.search([('refund_invoice_id', '=', vendor_invoice.id)], order='id desc', limit=1) + if invoice_id: + invoice_id.intercompany_transfer_id = self.id + for invoice_line in invoice_id.invoice_line_ids: + match_line = self.intercompany_transferline_ids.filtered(lambda ln : ln.product_id.id == invoice_line.product_id.id) + if match_line: + invoice_line.quantity = match_line.quantity + if invoice_id.state == "draft": + invoice_id.with_context({'active_ids':vendor_invoice.id}).action_invoice_open() + + self.write({'state':'processed'}) + return True + + + def action_create_reverse_process(self): + reverse_ict_line_obj = self.env['reverse.inter.company.transfer.line.ept'] + created_reverse_ids = [] + + for line in self.intercompany_transferline_ids: + if line.qty_delivered != 0.0: + qty_delivered = line.qty_delivered + if line.qty_delivered <= line.quantity: + inter_company_transfer_ids = self.search([('intercompany_transfer_id', '=', self.id), ('type', '=', 'ict_reverse'), ('state', '!=', 'cancel')]) + if inter_company_transfer_ids: + qty_delivered = 0.0 + total_qty_deliverd = 0.0 + if len(inter_company_transfer_ids) > 1: + for inter_company_id in inter_company_transfer_ids: + for transferline in inter_company_id.intercompany_transferline_ids: + if transferline.product_id == line.product_id: + total_qty_deliverd += transferline.quantity + qty_delivered = line.qty_delivered - total_qty_deliverd + if qty_delivered != 0.0 and qty_delivered > 0.0: + created_reverse_ids.append(reverse_ict_line_obj.create({'product_id':line.product_id.id, 'quantity':qty_delivered , 'qty_delivered': qty_delivered, 'price' : line.price}).id) + else: + for already_line in inter_company_transfer_ids.intercompany_transferline_ids: + if already_line.product_id == line.product_id: + if already_line.quantity == line.quantity: + continue + elif already_line.quantity < line.quantity: + qty_delivered = line.qty_delivered - already_line.quantity + if qty_delivered != 0.0 and qty_delivered > 0.0: + created_reverse_ids.append(reverse_ict_line_obj.create({'product_id':line.product_id.id, 'quantity':qty_delivered, 'qty_delivered': qty_delivered, 'price' : line.price}).id) + else: + created_reverse_ids.append(reverse_ict_line_obj.create({'product_id':line.product_id.id, 'quantity':qty_delivered , 'qty_delivered': qty_delivered, 'price' : line.price}).id) + else: + created_reverse_ids.append(reverse_ict_line_obj.create({'product_id':line.product_id.id, 'quantity':qty_delivered , 'qty_delivered': qty_delivered, 'price' : line.price}).id) + else: + continue + if created_reverse_ids: + return { + 'type': 'ir.actions.act_window', + 'res_model': 'reverse.inter.company.transfer.ept', + 'view_type': 'form', + 'view_mode': 'form', + 'context' : {'default_intercompany_transfer_id':self.id, 'default_reverse_intercompanyline_ids':[(6, 0, created_reverse_ids)], + 'default_destination_warehouse':self.destination_warehouse_id and self.destination_warehouse_id.id or False}, + 'target': 'new', + + } + else: + raise Warning("There are no products found for the Reverse Transaction !!") + + + def action_cancel(self): + saleorder_ids = self.saleorder_ids.filtered(lambda so : so.state == 'draft') + puchaseorder_ids = self.purchaseorder_ids.filtered(lambda po:po.state == 'draft') + invoice_ids = self.invoice_ids.filtered(lambda inv: inv.state == 'draft') + picking_ids = self.picking_ids.filtered(lambda pick:pick.state == 'draft') + if self.state == 'processed': + if saleorder_ids and puchaseorder_ids and invoice_ids and picking_ids: + saleorder_ids.action_cancel() + puchaseorder_ids.action_cancel() + invoice_ids.action_cancel() + picking_ids.action_cancel() + self.reset_to_draft() + else: + raise Warning("You Can not Cancel Inter Company Transaction Which All Transaction State is Done") + else: + self.write({'state':'cancel', 'message' : 'ICT has been cancelled by %s' % (self.env.user.name) }) + + + + def reset_to_draft(self): + self.ensure_one() + self.state = 'draft' + + + def unlink(self): + picking_ids = [picking.state != 'done' for picking in self.picking_ids] + if picking_ids and self.state == 'processed': + raise Warning("You can not delete transaction, if it is in Processed state !!") + res = super(InterCompanyTransfer, self).unlink() + return res + + + + def view_sale_order(self): + form_id = self.env.ref('sale.view_order_form').id + tree_id = self.env.ref('sale.view_order_tree').id + resource_ids = self.saleorder_ids and self.saleorder_ids.ids or [] + action = { + 'name': 'Sale Order', + 'type': 'ir.actions.act_window', + 'view_type': 'form', + 'res_model': 'sale.order', + 'domain':[('id', 'in', resource_ids)] + } + return self._open_form_tree_view(action, form_id, tree_id, resource_ids) + + + def view_reverse_ict(self): + form_id = self.env.ref('intercompany_transaction_ept.inter_company_transfer_ept_form_view').id + tree_id = self.env.ref('intercompany_transaction_ept.inter_company_transfer_ept_tree_view').id + resource_ids = self.reverse_intercompanytransfer_ids.ids + action = { + 'name': 'Reverse ICT', + 'type': 'ir.actions.act_window', + 'view_type': 'form', + 'res_model': 'inter.company.transfer.ept', + 'domain':[('id', 'in', resource_ids)] + } + return self._open_form_tree_view(action, form_id, tree_id, resource_ids) + + + + def view_purchase_order(self): + form_id = self.env.ref('purchase.purchase_order_form').id + tree_id = self.env.ref('purchase.purchase_order_tree').id + resource_ids = self.purchaseorder_ids and self.purchaseorder_ids.ids or [] + action = { + 'name': 'Purchase Order', + 'type': 'ir.actions.act_window', + 'view_type': 'form', + 'view_mode': 'tree,form', + 'res_model': 'purchase.order', + 'domain':[('id', 'in', resource_ids)] + } + return self._open_form_tree_view(action, form_id, tree_id, resource_ids) + + + def view_pickings(self): + form_id = self.env.ref('stock.view_picking_form').id + tree_id = self.env.ref('stock.vpicktree').id + resource_ids = self.picking_ids and self.picking_ids.ids or [] + action = { + 'name':_('Pickings'), + 'type': 'ir.actions.act_window', + 'view_type': 'form', + 'res_model': 'stock.picking', + 'domain':[('id', 'in', resource_ids)] + } + + return self._open_form_tree_view(action, form_id, tree_id, resource_ids) + + + def view_invoice(self): + tree_id = self.env.ref('account.invoice_tree').id + form_id = self.env.ref('account.invoice_form').id + resource_ids = self.env['account.move'].search([('intercompany_transfer_id', '=', self.id), ('type', '=', 'out_refund')]).ids or [] + action = { + 'name': _('Customer Invoice'), + 'type': 'ir.actions.act_window', + 'view_type': 'form', + 'res_model': 'account.move', + 'target':'current', + 'domain':[('id', 'in', resource_ids)] + } + return self._open_form_tree_view(action, form_id, tree_id, resource_ids) + + + def view_vendor_bill(self): + tree_id = self.env.ref('account.invoice_supplier_tree').id + form_id = self.env.ref('account.invoice_supplier_form').id + resource_ids = self.env['account.move'].search([('intercompany_transfer_id', '=', self.id), ('type', '=', 'in_refund')]).ids or [] + action = { + 'name': _('Vendor Bill'), + 'type': 'ir.actions.act_window', + 'view_type': 'form', + 'res_model': 'account.move', + 'target':'current', + 'domain':[('id', 'in', resource_ids)] + } + return self._open_form_tree_view(action, form_id, tree_id, resource_ids) + + + + + def action_view_log(self): + tree_id = self.env.ref('intercompany_transaction_ept.inter_company_transfer_process_log_tree_view').id + form_id = self.env.ref('intercompany_transaction_ept.inter_company_transfer_process_log_form_view').id + resource_ids = self.log_ids.ids + action = { + 'name': 'ICT Log', + 'type': 'ir.actions.act_window', + 'view_type': 'form', + 'res_model': 'inter.company.transfer.log.ept', + 'domain':[('id', 'in', resource_ids)] + } + return self._open_form_tree_view(action, form_id, tree_id, resource_ids) + + + + def _open_form_tree_view(self, action, form_view_id, tree_view_id, resource_ids): + if len(resource_ids) == 1 : + action.update({'view_id':form_view_id, + 'res_id':resource_ids[0], + 'view_mode': 'form'}) + else : + action.update({'view_id':False, + 'view_mode': 'tree,form', + 'views': [(tree_view_id, 'tree'), (form_view_id, 'form')]}) + + return action + + def import_export_product_list_view(self): + form_id = self.env.ref('intercompany_transaction_ept.import_export_product_list_ept_form_view').id + ctx = self._context.copy() or {} + ctx.update({'record':self.id or False}) + return { + 'type': 'ir.actions.act_window', + 'view_type': 'form', + 'view_mode': 'form', + 'res_model': 'import.export.product.list.ept', + 'views': [(form_id, 'form')], + 'view_id': form_id, + 'target': 'new', + 'context': ctx, + } + + + + + + + + + + + diff --git a/intercompany_transaction_ept/models/inter_company_transfer_line_ept.py b/intercompany_transaction_ept/models/inter_company_transfer_line_ept.py new file mode 100644 index 0000000..b00b7ad --- /dev/null +++ b/intercompany_transaction_ept/models/inter_company_transfer_line_ept.py @@ -0,0 +1,54 @@ +from odoo import models, fields, api, _ +from odoo.exceptions import UserError, ValidationError +from datetime import datetime +from odoo.addons import decimal_precision as dp + + + +class InterCompanyTransferLine(models.Model): + + _name = "inter.company.transfer.line.ept" + _description = "Internal Company Transfer Line" + + + @api.depends( 'inter_transfer_id.picking_ids.state') + def _get_delivered_qty(self): + for line in self: + if line.inter_transfer_id.state in ['processed']: + qty_delivered = 0.0 + for picking_id in line.inter_transfer_id.picking_ids: + if picking_id.state != 'cancel': + for move_line in picking_id.move_ids_without_package: + if line.product_id == move_line.product_id: + if picking_id.picking_type_id.code == 'incoming': + qty_delivered += move_line.product_id.uom_id._compute_quantity(move_line.quantity_done, move_line.product_id.uom_id) + line.qty_delivered = qty_delivered + else: + line.qty_delivered = 0.0 + + + quantity = fields.Float(string="Quantity", default=1.0) + qty_delivered = fields.Float(compute='_get_delivered_qty', string='Delivered Quantity', store=True, readonly=True, digits=dp.get_precision('Product Unit of Measure')) + price = fields.Float(string='Price') + product_id = fields.Many2one('product.product', string='Product') + inter_transfer_id = fields.Many2one('inter.company.transfer.ept') + + + @api.onchange('product_id', 'inter_transfer_id') + def default_price_get(self): + """ + Get the Product Price + """ + for record in self: + product_id = record.product_id + if product_id: + pricelist_id = record.inter_transfer_id.price_list_id + if pricelist_id: + record.price = pricelist_id.price_get(product_id.id, record.quantity)[pricelist_id.id] + else: + record.price = record.product_id.lst_price + else: + record.price = 0.0 + + + diff --git a/intercompany_transaction_ept/models/inter_company_transfer_log_process_ept.py b/intercompany_transaction_ept/models/inter_company_transfer_log_process_ept.py new file mode 100755 index 0000000..d1d6e59 --- /dev/null +++ b/intercompany_transaction_ept/models/inter_company_transfer_log_process_ept.py @@ -0,0 +1,59 @@ +from odoo import models, fields, api, _ +from odoo.exceptions import UserError, ValidationError +from datetime import datetime +import datetime + +module_name = [('inter.company.transfer.ept', 'Inter Company Transfer')] + +class InterCompanyTransferLog(models.Model): + _name = 'inter.company.transfer.log.ept' + _description = 'Inter company Transfer Log Process' + + + name = fields.Char(string="Name") + ict_log_date = fields.Datetime(string="Log Date") + ict_process = fields.Selection(module_name, string="Application") + ict_operation = fields.Selection([('import', 'Import')], string="Operation") + intercompany_transfer_id = fields.Many2one('inter.company.transfer.ept', string="Inter company Transfer ID") + + ict_log_line_ids = fields.One2many('inter.company.transfer.log.line.ept', 'ict_log_id') + + def return_log_record(self, model_name, operation_type): + sequence_id = self.env.ref('intercompany_transaction_ept.inter_company_process_log_seq').ids + if sequence_id: + record_name = self.env['ir.sequence'].browse(sequence_id).next_by_id() + else: + record_name = '/' + log_vals = { + 'name' : record_name, + 'ict_log_date': datetime.datetime.now(), + 'ict_process':model_name._name, + 'ict_operation':operation_type, + 'intercompany_transfer_id':model_name.id + } + log_record = self.create(log_vals) + + return log_record + + def post_log_line(self, message , type='error'): + self.ensure_one() + intercompany_transfer_logline_obj = self.env['inter.company.transfer.log.line.ept'] + log_line_vals = { + 'ict_message':message, + 'ict_log_type': type, + 'ict_log_id': self.id, + } + log_line_id = intercompany_transfer_logline_obj.create(log_line_vals) + return log_line_id + + +class InterCompanyTransferLogLine(models.Model): + + _name = "inter.company.transfer.log.line.ept" + _description = 'Inter company Transfer Log Line Process' + _rec_name = "ict_log_id" + + ict_message = fields.Text(string="Message") + ict_log_type = fields.Selection([('error', 'Error'), ('mismatch', 'Mismatch'), ('info', 'Info')], string="Log Type") + ict_log_id = fields.Many2one('inter.company.transfer.log.ept', string="ICT Process Log", ondelete='cascade') + diff --git a/intercompany_transaction_ept/models/purchase.py b/intercompany_transaction_ept/models/purchase.py new file mode 100644 index 0000000..0a084a4 --- /dev/null +++ b/intercompany_transaction_ept/models/purchase.py @@ -0,0 +1,7 @@ +from odoo import fields, api, models , _ + +class PurchaseOrder(models.Model): + _inherit = 'purchase.order' + _description = 'Purchase Order' + + intercompany_transfer_id = fields.Many2one('inter.company.transfer.ept', string="ICT", copy=False) diff --git a/intercompany_transaction_ept/models/res_company.py b/intercompany_transaction_ept/models/res_company.py new file mode 100644 index 0000000..987765c --- /dev/null +++ b/intercompany_transaction_ept/models/res_company.py @@ -0,0 +1,9 @@ +from odoo import fields, models + +class ResCompany(models.Model): + _inherit = "res.company" + _description = 'Res Company' + + intercompany_user_id = fields.Many2one('res.users', string="Intercompany User") + sale_journal = fields.Many2one('account.journal', string="Sale Journal") + purchase_journal = fields.Many2one('account.journal', string="Purchase Journal") diff --git a/intercompany_transaction_ept/models/sale.py b/intercompany_transaction_ept/models/sale.py new file mode 100644 index 0000000..f3f4f60 --- /dev/null +++ b/intercompany_transaction_ept/models/sale.py @@ -0,0 +1,21 @@ +from odoo import api, fields, models, _ + +class SaleOrder(models.Model): + _inherit = 'sale.order' + _description = 'Sale Order' + + intercompany_transfer_id = fields.Many2one('inter.company.transfer.ept', string="ICT", copy=False) + + + """ + This Method is used to invoice journal issue when it is creating time(as breadcrumb) + """ + + def _prepare_invoice(self): + if self.intercompany_transfer_id: + journal_id = self.env['account.invoice'].default_get(['journal_id'])['journal_id'] + vals = super(SaleOrder, self.with_context({'journal_id':journal_id}))._prepare_invoice() + return vals + else: + vals = super(SaleOrder, self)._prepare_invoice() + return vals \ No newline at end of file diff --git a/intercompany_transaction_ept/models/stock_picking.py b/intercompany_transaction_ept/models/stock_picking.py new file mode 100644 index 0000000..6dc6008 --- /dev/null +++ b/intercompany_transaction_ept/models/stock_picking.py @@ -0,0 +1,24 @@ +from odoo import fields, api, models, _ + +class Picking(models.Model): + _inherit = 'stock.picking' + _description = 'Stock Picking' + + intercompany_transfer_id = fields.Many2one('inter.company.transfer.ept', string="ICT", copy=False) + + @api.model + def create(self, vals): + res = super(Picking, self).create(vals) + order_id = self.env['sale.order'].search([('name', '=', res.origin)]) + if not order_id: + order_id = self.env['purchase.order'].search([('name', '=', res.origin)]) + if order_id and order_id.intercompany_transfer_id: + res.intercompany_transfer_id = order_id.intercompany_transfer_id.id + return res + + def _create_backorder(self): + res = super(Picking, self)._create_backorder() + for backorder in res: + if backorder.backorder_id and backorder.backorder_id.intercompany_transfer_id: + backorder.write({"intercompany_transfer_id":backorder.backorder_id.intercompany_transfer_id.id}) + return res \ No newline at end of file diff --git a/intercompany_transaction_ept/report/__init__.py b/intercompany_transaction_ept/report/__init__.py new file mode 100644 index 0000000..cd23411 --- /dev/null +++ b/intercompany_transaction_ept/report/__init__.py @@ -0,0 +1 @@ +from . import sale_report diff --git a/intercompany_transaction_ept/report/__pycache__/__init__.cpython-36.pyc b/intercompany_transaction_ept/report/__pycache__/__init__.cpython-36.pyc new file mode 100644 index 0000000..010ba86 Binary files /dev/null and b/intercompany_transaction_ept/report/__pycache__/__init__.cpython-36.pyc differ diff --git a/intercompany_transaction_ept/report/__pycache__/sale_report.cpython-36.pyc b/intercompany_transaction_ept/report/__pycache__/sale_report.cpython-36.pyc new file mode 100644 index 0000000..298bf0f Binary files /dev/null and b/intercompany_transaction_ept/report/__pycache__/sale_report.cpython-36.pyc differ diff --git a/intercompany_transaction_ept/report/sale_report.py b/intercompany_transaction_ept/report/sale_report.py new file mode 100644 index 0000000..68e0ca2 --- /dev/null +++ b/intercompany_transaction_ept/report/sale_report.py @@ -0,0 +1,17 @@ +from odoo import models,fields + +class SaleReport(models.Model): + _inherit = "sale.report" + + intercompany_transfer_id = fields.Many2one('inter.company.transfer.ept', string="ICT", copy=False) + + def _select(self): + qry = super(SaleReport,self)._select() + + qry += ', s.intercompany_transfer_id as intercompany_transfer_id ' + return qry + + def _group_by(self): + qry = super(SaleReport,self)._group_by() + qry += ', s.intercompany_transfer_id ' + return qry \ No newline at end of file diff --git a/intercompany_transaction_ept/security/inter_company_transfer_security.xml b/intercompany_transaction_ept/security/inter_company_transfer_security.xml new file mode 100644 index 0000000..b3a0d6d --- /dev/null +++ b/intercompany_transaction_ept/security/inter_company_transfer_security.xml @@ -0,0 +1,82 @@ + + + + + Inter Company Transfer + Inter Company Transfer User Access Rights + 100 + + + + Inter Company Transfer User + + + + + + + + + Inter Company Transfer Manager + + + + + + + + + + Inter Company Transfer Multi Company + + ['|', ('source_warehouse_id', '=', False),'|',('destination_warehouse_id','=',False), '|',('source_warehouse_id.company_id','=',False),'|',('source_warehouse_id.company_id','child_of',[user.company_id.id]),'|',('destination_warehouse_id.company_id','=',False),('destination_warehouse_id.company_id','child_of',[user.company_id.id])] + + + + + + + + Inter Company Transfer Log + + ['|', ('intercompany_transfer_id', '=',False),'|',('intercompany_transfer_id.source_warehouse_id', '=',False),'|',('intercompany_transfer_id.destination_warehouse_id','=',False), '|',('intercompany_transfer_id.source_warehouse_id.company_id','=',False),'|',('intercompany_transfer_id.source_warehouse_id.company_id','child_of',[user.company_id.id]),'|',('intercompany_transfer_id.destination_warehouse_id.company_id','=',False),('intercompany_transfer_id.destination_warehouse_id.company_id','child_of',[user.company_id.id])] + + + + + + + + Inter Company Transfer Warehouse Multi Company All Allow Company + + ['|',('company_id','=',False),('company_id','child_of',user.company_ids and user.company_ids.ids or [user.company_id.id])] + + + + + + + + + Inter Company Transfer Location Multi Company All Allow Company + + ['|',('company_id','=',False),('company_id','child_of',user.company_ids and user.company_ids.ids or [user.company_id.id])] + + + + + + + + + Inter Company Transfer Stock Operation Type multi-company all + + ['|', ('warehouse_id', '=', False), '|',('warehouse_id.company_id','=',False),('warehouse_id.company_id','child_of',user.company_ids and user.company_ids.ids or [user.company_id.id])] + + + + + + + + diff --git a/intercompany_transaction_ept/security/ir.model.access.csv b/intercompany_transaction_ept/security/ir.model.access.csv new file mode 100644 index 0000000..d24b3cf --- /dev/null +++ b/intercompany_transaction_ept/security/ir.model.access.csv @@ -0,0 +1,13 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_inter_company_transfer_ept_manager,inter.company.transfer.ept.manager,model_inter_company_transfer_ept,intercompany_transfer_manager_group,1,1,1,1 +access_inter_company_transfer_ept_user,inter.company.transfer.ept.user,model_inter_company_transfer_ept,intercompany_transfer_user_group,1,1,1,0 +access_inter_company_transfer_line_ept_manager,inter.company.transfer.line.ept.manager,model_inter_company_transfer_line_ept,intercompany_transfer_manager_group,1,1,1,1 +access_inter_company_transfer_line_ept_user,inter.company.transfer.line.ept.user,model_inter_company_transfer_line_ept,intercompany_transfer_user_group,1,1,1,0 +access_inter_company_config_ept_manager,inter.company.config.ept.manager,model_inter_company_transfer_config_ept,intercompany_transfer_manager_group,1,1,1,1 +access_inter_company_config_ept_user,inter.company.config.ept.user,model_inter_company_transfer_config_ept,intercompany_transfer_user_group,1,1,1,0 +access_sale_advance_payment_inv_manager,sale.advance.payment.inv.manager,sale.model_sale_advance_payment_inv,intercompany_transfer_manager_group,1,1,1,1 +access_sale_advance_payment_inv_user,sale.advance.payment.inv.user,sale.model_sale_advance_payment_inv,intercompany_transfer_user_group,1,1,1,0 +access_inter_company_transfer_process_log_manager,Inter.company.transfer.process.log.manager,model_inter_company_transfer_log_ept,intercompany_transfer_manager_group,1,1,1,1 +access_inter_company_transfer_process_log_user,Inter.company.transfer.process.log.user,model_inter_company_transfer_log_ept,intercompany_transfer_user_group,1,1,1,0 +access_inter_company_transfer_process_log_line_manager,Inter.company.transfer.process.log.line.manager,model_inter_company_transfer_log_line_ept,intercompany_transfer_manager_group,1,1,1,1 +access_inter_company_transfer_process_log_line_user,Inter.company.transfer.process.log.line.user,model_inter_company_transfer_log_line_ept,intercompany_transfer_user_group,1,1,1,0 diff --git a/intercompany_transaction_ept/static/demo_data/importproductlist.csv b/intercompany_transaction_ept/static/demo_data/importproductlist.csv new file mode 100644 index 0000000..36aff02 --- /dev/null +++ b/intercompany_transaction_ept/static/demo_data/importproductlist.csv @@ -0,0 +1,4 @@ +Default Code,Qty,Price +2030020100075,5,400 +2030015000007,4,500 +2030030100003,8,2000 diff --git a/intercompany_transaction_ept/static/demo_data/importproductlist.xls b/intercompany_transaction_ept/static/demo_data/importproductlist.xls new file mode 100644 index 0000000..15b329a Binary files /dev/null and b/intercompany_transaction_ept/static/demo_data/importproductlist.xls differ diff --git a/intercompany_transaction_ept/static/description/Advance-Purchase-Ordering-Reordering.jpg b/intercompany_transaction_ept/static/description/Advance-Purchase-Ordering-Reordering.jpg new file mode 100755 index 0000000..b0ac3e0 Binary files /dev/null and b/intercompany_transaction_ept/static/description/Advance-Purchase-Ordering-Reordering.jpg differ diff --git a/intercompany_transaction_ept/static/description/All-in-One-Inventory-Reports.jpg b/intercompany_transaction_ept/static/description/All-in-One-Inventory-Reports.jpg new file mode 100644 index 0000000..4c754b7 Binary files /dev/null and b/intercompany_transaction_ept/static/description/All-in-One-Inventory-Reports.jpg differ diff --git a/intercompany_transaction_ept/static/description/All-in-One-Solution-for-Amazon-Seller.jpg b/intercompany_transaction_ept/static/description/All-in-One-Solution-for-Amazon-Seller.jpg new file mode 100644 index 0000000..5acabbc Binary files /dev/null and b/intercompany_transaction_ept/static/description/All-in-One-Solution-for-Amazon-Seller.jpg differ diff --git a/intercompany_transaction_ept/static/description/Amazon-Vendor-Central-Integration.jpg b/intercompany_transaction_ept/static/description/Amazon-Vendor-Central-Integration.jpg new file mode 100644 index 0000000..8a0e795 Binary files /dev/null and b/intercompany_transaction_ept/static/description/Amazon-Vendor-Central-Integration.jpg differ diff --git a/intercompany_transaction_ept/static/description/Automated-Sales-Order-Processing.jpg b/intercompany_transaction_ept/static/description/Automated-Sales-Order-Processing.jpg new file mode 100644 index 0000000..e04a086 Binary files /dev/null and b/intercompany_transaction_ept/static/description/Automated-Sales-Order-Processing.jpg differ diff --git a/intercompany_transaction_ept/static/description/Automatic-Documents-Creation.png b/intercompany_transaction_ept/static/description/Automatic-Documents-Creation.png new file mode 100644 index 0000000..b39ef6d Binary files /dev/null and b/intercompany_transaction_ept/static/description/Automatic-Documents-Creation.png differ diff --git a/intercompany_transaction_ept/static/description/Automatic-Workflow.png b/intercompany_transaction_ept/static/description/Automatic-Workflow.png new file mode 100644 index 0000000..a63799a Binary files /dev/null and b/intercompany_transaction_ept/static/description/Automatic-Workflow.png differ diff --git a/intercompany_transaction_ept/static/description/CRM-Lead-Age-&-Lead-Age-Breakdown-Report.jpg b/intercompany_transaction_ept/static/description/CRM-Lead-Age-&-Lead-Age-Breakdown-Report.jpg new file mode 100644 index 0000000..36fdb5d Binary files /dev/null and b/intercompany_transaction_ept/static/description/CRM-Lead-Age-&-Lead-Age-Breakdown-Report.jpg differ diff --git a/intercompany_transaction_ept/static/description/Consignment-Management.jpg b/intercompany_transaction_ept/static/description/Consignment-Management.jpg new file mode 100644 index 0000000..4c69838 Binary files /dev/null and b/intercompany_transaction_ept/static/description/Consignment-Management.jpg differ diff --git a/intercompany_transaction_ept/static/description/Control-over-pricing-and-invoicing.png b/intercompany_transaction_ept/static/description/Control-over-pricing-and-invoicing.png new file mode 100644 index 0000000..1501e8f Binary files /dev/null and b/intercompany_transaction_ept/static/description/Control-over-pricing-and-invoicing.png differ diff --git a/intercompany_transaction_ept/static/description/Dropshipper-EDI-Integration-in-Odoo.jpg b/intercompany_transaction_ept/static/description/Dropshipper-EDI-Integration-in-Odoo.jpg new file mode 100644 index 0000000..9c511b9 Binary files /dev/null and b/intercompany_transaction_ept/static/description/Dropshipper-EDI-Integration-in-Odoo.jpg differ diff --git a/intercompany_transaction_ept/static/description/Dropshipping-EDI-Integration-in-Odoo.jpg b/intercompany_transaction_ept/static/description/Dropshipping-EDI-Integration-in-Odoo.jpg new file mode 100644 index 0000000..c4cc537 Binary files /dev/null and b/intercompany_transaction_ept/static/description/Dropshipping-EDI-Integration-in-Odoo.jpg differ diff --git a/intercompany_transaction_ept/static/description/Easy-Interface-to-Exchange-Stock.png b/intercompany_transaction_ept/static/description/Easy-Interface-to-Exchange-Stock.png new file mode 100644 index 0000000..1d935f5 Binary files /dev/null and b/intercompany_transaction_ept/static/description/Easy-Interface-to-Exchange-Stock.png differ diff --git a/intercompany_transaction_ept/static/description/Easy-Interface-to-Return-Stock.png b/intercompany_transaction_ept/static/description/Easy-Interface-to-Return-Stock.png new file mode 100644 index 0000000..a5b3bf4 Binary files /dev/null and b/intercompany_transaction_ept/static/description/Easy-Interface-to-Return-Stock.png differ diff --git a/intercompany_transaction_ept/static/description/Easy-Navigation-between-created-document.png b/intercompany_transaction_ept/static/description/Easy-Navigation-between-created-document.png new file mode 100644 index 0000000..b8ed743 Binary files /dev/null and b/intercompany_transaction_ept/static/description/Easy-Navigation-between-created-document.png differ diff --git a/intercompany_transaction_ept/static/description/Feedback-email-to-customers-for product review.jpg b/intercompany_transaction_ept/static/description/Feedback-email-to-customers-for product review.jpg new file mode 100644 index 0000000..aa2dc83 Binary files /dev/null and b/intercompany_transaction_ept/static/description/Feedback-email-to-customers-for product review.jpg differ diff --git a/intercompany_transaction_ept/static/description/Inter-Company-Transfer-cover.png b/intercompany_transaction_ept/static/description/Inter-Company-Transfer-cover.png new file mode 100644 index 0000000..bfe2ff0 Binary files /dev/null and b/intercompany_transaction_ept/static/description/Inter-Company-Transfer-cover.png differ diff --git a/intercompany_transaction_ept/static/description/Inter-Company-Transfer.png b/intercompany_transaction_ept/static/description/Inter-Company-Transfer.png new file mode 100644 index 0000000..37e46a8 Binary files /dev/null and b/intercompany_transaction_ept/static/description/Inter-Company-Transfer.png differ diff --git a/intercompany_transaction_ept/static/description/Inventory-Covarge-Report.jpg b/intercompany_transaction_ept/static/description/Inventory-Covarge-Report.jpg new file mode 100644 index 0000000..cbbd034 Binary files /dev/null and b/intercompany_transaction_ept/static/description/Inventory-Covarge-Report.jpg differ diff --git a/intercompany_transaction_ept/static/description/Manage-Sale-Promotions-in-Odoo.jpg b/intercompany_transaction_ept/static/description/Manage-Sale-Promotions-in-Odoo.jpg new file mode 100644 index 0000000..28f470c Binary files /dev/null and b/intercompany_transaction_ept/static/description/Manage-Sale-Promotions-in-Odoo.jpg differ diff --git a/intercompany_transaction_ept/static/description/Product-Profitability-Report.jpg b/intercompany_transaction_ept/static/description/Product-Profitability-Report.jpg new file mode 100644 index 0000000..bf2517d Binary files /dev/null and b/intercompany_transaction_ept/static/description/Product-Profitability-Report.jpg differ diff --git a/intercompany_transaction_ept/static/description/README.rst b/intercompany_transaction_ept/static/description/README.rst new file mode 100644 index 0000000..84d3159 --- /dev/null +++ b/intercompany_transaction_ept/static/description/README.rst @@ -0,0 +1,38 @@ +================================================= +Inter Company Transfer and Warehouse Transfer +================================================= + +With the help of this App one can manage multi company inter transfer & Internal Transfer. Local and consolidated reporting along with documents can be automatically managed with simple configuration using our App. + + +================================================= +Inter Company Transfer +================================================= +Creates Sales Order from source warehouse & Purchase Order in destination warehouse if warehouses belongs to different companies. +Control over Invoice numbering / Refund numbering by specifying separate journals company wise. + System will allow you to control pricing from the same screen. Same price will apply on sales order of source company and purchase order of destination company. +An option to reverse entire / partial transactions with all effects. In case of Reverse intercompany transactions system will create reverse of delivery order, reverse of incoming shipment (only if they are done), debit note and credit note. +Control over all documents process like automatic workflow of all documents, whether need to create or not, auto confirm or not, auto validate or not. Like one can control Auto confirm Sales Orders/ Purchase Order or Generate & Validate Invoice and Cancel: create credit note and reconcile. +In multi company environment it’s not easy to create documents of multi companies without login to that company, with this app user can create all records related to intercompany transactions by just one click without login to that company, for that just need to set intercompany user company wise. So you can be sure that taxes applied on SO & PO are accurate according to the companies. + + +================================================= +Inter Warehouse Transfer (Internal Transfer) +================================================= +Creates Delivery Order in source warehouse & Receipts in Destination warehouse. +Smart buttons to view created Delivery Order & Receipt. +Allows users to do reverse transfer from the same screen. It will reverse the delivery order & receipt. + + +============ +Similar Apps +============ +Automated Sales Order Processing +Website RMA ( Return Merchandise Authorization ) in Odoo +All in One Inventory Reports +All in One Solution for Amazon Seller +Product Profitability Report +Dropshipper EDI Integration in Odoo +CRM Lead Age & Lead Age Breakdown Report +RMA ( Return Merchandise Authorisation ) in Odoo +Stock level indicator in Sales Quotation diff --git a/intercompany_transaction_ept/static/description/Redirect.png b/intercompany_transaction_ept/static/description/Redirect.png new file mode 100644 index 0000000..2e853bb Binary files /dev/null and b/intercompany_transaction_ept/static/description/Redirect.png differ diff --git a/intercompany_transaction_ept/static/description/Return-Order-Management.jpg b/intercompany_transaction_ept/static/description/Return-Order-Management.jpg new file mode 100644 index 0000000..f09c9cb Binary files /dev/null and b/intercompany_transaction_ept/static/description/Return-Order-Management.jpg differ diff --git a/intercompany_transaction_ept/static/description/Sale-Margin-Report.jpg b/intercompany_transaction_ept/static/description/Sale-Margin-Report.jpg new file mode 100644 index 0000000..c5b4b43 Binary files /dev/null and b/intercompany_transaction_ept/static/description/Sale-Margin-Report.jpg differ diff --git a/intercompany_transaction_ept/static/description/Stock-level-indicator-in-Sales-Quotation.jpg b/intercompany_transaction_ept/static/description/Stock-level-indicator-in-Sales-Quotation.jpg new file mode 100644 index 0000000..2b10aba Binary files /dev/null and b/intercompany_transaction_ept/static/description/Stock-level-indicator-in-Sales-Quotation.jpg differ diff --git a/intercompany_transaction_ept/static/description/Top-Growing-Products-Report.jpg b/intercompany_transaction_ept/static/description/Top-Growing-Products-Report.jpg new file mode 100644 index 0000000..c136006 Binary files /dev/null and b/intercompany_transaction_ept/static/description/Top-Growing-Products-Report.jpg differ diff --git a/intercompany_transaction_ept/static/description/User-Manual-Icon.png b/intercompany_transaction_ept/static/description/User-Manual-Icon.png new file mode 100644 index 0000000..bae66ed Binary files /dev/null and b/intercompany_transaction_ept/static/description/User-Manual-Icon.png differ diff --git a/intercompany_transaction_ept/static/description/Video.png b/intercompany_transaction_ept/static/description/Video.png new file mode 100644 index 0000000..1002ec0 Binary files /dev/null and b/intercompany_transaction_ept/static/description/Video.png differ diff --git a/intercompany_transaction_ept/static/description/Website-RMA-(Return Merchandise Authorization) in Odoo.jpg b/intercompany_transaction_ept/static/description/Website-RMA-(Return Merchandise Authorization) in Odoo.jpg new file mode 100644 index 0000000..c2d0f1e Binary files /dev/null and b/intercompany_transaction_ept/static/description/Website-RMA-(Return Merchandise Authorization) in Odoo.jpg differ diff --git a/intercompany_transaction_ept/static/description/emipro_logo.png b/intercompany_transaction_ept/static/description/emipro_logo.png new file mode 100644 index 0000000..2916e7a Binary files /dev/null and b/intercompany_transaction_ept/static/description/emipro_logo.png differ diff --git a/intercompany_transaction_ept/static/description/ept_odoo_partner.png b/intercompany_transaction_ept/static/description/ept_odoo_partner.png new file mode 100644 index 0000000..fc85b85 Binary files /dev/null and b/intercompany_transaction_ept/static/description/ept_odoo_partner.png differ diff --git a/intercompany_transaction_ept/static/description/icon.png b/intercompany_transaction_ept/static/description/icon.png new file mode 100644 index 0000000..e806f5d Binary files /dev/null and b/intercompany_transaction_ept/static/description/icon.png differ diff --git a/intercompany_transaction_ept/static/description/index.html b/intercompany_transaction_ept/static/description/index.html new file mode 100644 index 0000000..567aa6f --- /dev/null +++ b/intercompany_transaction_ept/static/description/index.html @@ -0,0 +1,740 @@ +
+
+
+
+ + + +
+
+
+ + + +
+
+
+
+
+
+ +

+ Inter Company Transfer and Warehouse Transfer +

+

+ The main purpose behind developing this app is to manage the product transfer between multiple companies and warehouses managed under the same roof. With auto document generation facility, the transfer operations are handled effectively within companies or partners. With this app, you can now track your stock more effectively while fulfilling the demands. +

+
+
+
+
+ +

Request a Free Demo

+
+
+
+
+
+

+ Highlights +

+
+
+
+
+ +
+
+

Easy Interface to Exchange Stock

+

+ Superior user experience to carry out exchange of stock between companies/warehouses. +

+
+
+
+
+
+
+ +
+
+

Easy Interface to Return Stock

+

+ Return the stock to source companies/warehouses with uncomplicated interface. +

+
+
+
+
+
+
+ +
+
+

Automatic Documents Creation

+

+ Documents are automatically generated after each transfers in your Odoo as a part of confirmation receipt. +

+
+
+
+
+
+
+ +
+
+

Easy Navigation between created Documents

+

+ Switch over to different documents without making effort to find them from large Odoo database.
+

+
+
+
+
+
+
+ +
+
+

Control over pricing and invoicing

+

+ Take the full command over managing prices & invoices for various types of transfers. +

+
+
+
+
+
+
+ +
+
+

Automatic Workflow

+

+ A higher-level way to organize tasks by automating its operations & minimize manual interventions. +

+
+
+
+
+
+
+
+
+ +
+ +
+
+ +
+
+

Imaginary Flow of Inter Company Transfer

+
+ +
+
+
+

Imaginary Flow of Inter Warehouse Transfer

+
+ +
+
+
+
+
+
+
+

Set user rights for ICT User

+
+ +
+
+

Basic ICT Configuration for Company

+
+ +
+
+

Configure Resupply Warehouse for ICT Transactions

+
+ +
+
+

Product Configuration for its ICT operations

+
+ +
+
+

Inter Company Transfer important configuration

+
+ +
+
+

Set Customer Taxes and Vendor Taxes

+
+ +
+
+

Set Fiscal Position for customers

+
+ +
+
+

Inter Company Transfer orders in Odoo

+
+ +
+
+

ICT Order in Draft Stage

+
+ +
+
+

Processing of ICT Order

+
+ +
+
+

Reverse Inter Company Transfer order in Odoo

+
+ +
+
+

Reverse ICT created for ICT Order

+
+ +
+
+

Reverse ICT Orders in Odoo

+
+ +
+
+

Reverse ICT Order in Draft Stage

+
+ +
+
+

Reverse ICT Order information

+
+ +
+
+

Processing of Reverse ICT

+
+ +
+
+

Internal Transfer (Inter Warehouse Transfer) Orders

+
+ +
+
+

Internal Transfer Order in draft stage

+
+ +
+
+

Processing of Internal Transfer Order

+
+ +
+
+

Pickings of Internal Transfer

+
+ +
+
+

Create Reverse Internal Transfer

+
+ +
+
+

Reverse Internal Transfer order ready to process

+
+ +
+
+

Reverse ICT for Internal Transfer in draft stage

+
+ +
+
+

Reverse ICT of Internal Transfer processed

+
+ +
+
+

Pickings generated for Reverse ICT of Internal Transfer

+
+ +
+
+

Import/Export Product List for ICT

+
+ +
+
+

Operation Logs of all ICT & Internal Transfer Transactions

+
+ +
+
+

ICT Process Log

+
+ +
+
+
+
+ +
+
+
+
+
+
+ +
+
+

+ Yes, the app works perfectly well with Odoo Enterprise (On-premise and Odoo.SH) as well as Community. Odoo Online (Cloud) does not allow installation of third-party apps and hence this app cannot be installed on Odoo Online. +

+
+
+
+
+
+
+ +
+
+

+ You can contact us + to request a customization in the app. +

+
+
+
+
+
+
+ +
+
+

+ Yes, you can avail a free upgrade & support services for the app. Kindly read our + support policy + to know the details. +

+
+
+
+
+
+
+ +
+
+

+ Inter-Company Transaction is a movement of stock between two warehouses which belongs to different companies of the same owner. In these type of transactions, one company will create a Sales Order and another company will create a Purchase Order. +

+
+
+
+
+
+
+ +
+
+

+ Inter-Warehouse Transaction is a movement of stock between two warehouses which belongs to the same company. In these type of transactions, only internal transfers between warehouses get created. +

+
+
+
+
+
+
+ +
+
+

+ Yes, it is necessary of the ICT User to have single company access as all transactions will be carried out by the ICT user. If the ICT user has multi-company access, it will create issues and may not work as expected. +

+
+
+
+
+
+
+ +
+
+

+ If you want to transfer stock between two warehouses of the same company with the help of transit location, routes must be there in the system and these routes will be managed by the system from the Resupply warehouses configurations. +

+
+
+
+
+
+
+ +
+
+

+ Yes, it is necessary to have shared Products & Partners among multiple companies in order to carry out ICT operations. +

+
+
+
+
+
+
+ +
+
+

+ Yes, the app allows you to configure automatic workflow for Inter-Company Transaction and Reverse Inter-Company Transaction. +

+
+
+
+
+
+
+ +
+
+

+ No, it's not possible. The app will create the relevant SO/PO for all Inter-Company Transactions. If you have a special scenario where you want to avoid this, you can request customization. +

+
+
+
+
+
+
+
+
+
+
+
+

+ Suggested Products +

+ + +
+
+
+ + +
+
diff --git a/intercompany_transaction_ept/static/description/inter-Warehouse-Transfer-(Intra-company-transfer).png b/intercompany_transaction_ept/static/description/inter-Warehouse-Transfer-(Intra-company-transfer).png new file mode 100644 index 0000000..5d2a9c9 Binary files /dev/null and b/intercompany_transaction_ept/static/description/inter-Warehouse-Transfer-(Intra-company-transfer).png differ diff --git a/intercompany_transaction_ept/static/description/screens/1(1-1).png b/intercompany_transaction_ept/static/description/screens/1(1-1).png new file mode 100644 index 0000000..85b3150 Binary files /dev/null and b/intercompany_transaction_ept/static/description/screens/1(1-1).png differ diff --git a/intercompany_transaction_ept/static/description/screens/10(3).png b/intercompany_transaction_ept/static/description/screens/10(3).png new file mode 100644 index 0000000..b3a76a0 Binary files /dev/null and b/intercompany_transaction_ept/static/description/screens/10(3).png differ diff --git a/intercompany_transaction_ept/static/description/screens/11(4).png b/intercompany_transaction_ept/static/description/screens/11(4).png new file mode 100644 index 0000000..499dcbc Binary files /dev/null and b/intercompany_transaction_ept/static/description/screens/11(4).png differ diff --git a/intercompany_transaction_ept/static/description/screens/12(5).png b/intercompany_transaction_ept/static/description/screens/12(5).png new file mode 100644 index 0000000..e371318 Binary files /dev/null and b/intercompany_transaction_ept/static/description/screens/12(5).png differ diff --git a/intercompany_transaction_ept/static/description/screens/13(6).png b/intercompany_transaction_ept/static/description/screens/13(6).png new file mode 100644 index 0000000..f3a2077 Binary files /dev/null and b/intercompany_transaction_ept/static/description/screens/13(6).png differ diff --git a/intercompany_transaction_ept/static/description/screens/14(7).png b/intercompany_transaction_ept/static/description/screens/14(7).png new file mode 100644 index 0000000..aa4e269 Binary files /dev/null and b/intercompany_transaction_ept/static/description/screens/14(7).png differ diff --git a/intercompany_transaction_ept/static/description/screens/15(8).png b/intercompany_transaction_ept/static/description/screens/15(8).png new file mode 100644 index 0000000..a402d74 Binary files /dev/null and b/intercompany_transaction_ept/static/description/screens/15(8).png differ diff --git a/intercompany_transaction_ept/static/description/screens/16(9).png b/intercompany_transaction_ept/static/description/screens/16(9).png new file mode 100644 index 0000000..0d9b23c Binary files /dev/null and b/intercompany_transaction_ept/static/description/screens/16(9).png differ diff --git a/intercompany_transaction_ept/static/description/screens/17(10).png b/intercompany_transaction_ept/static/description/screens/17(10).png new file mode 100644 index 0000000..d69c38c Binary files /dev/null and b/intercompany_transaction_ept/static/description/screens/17(10).png differ diff --git a/intercompany_transaction_ept/static/description/screens/18(11).png b/intercompany_transaction_ept/static/description/screens/18(11).png new file mode 100644 index 0000000..25f4e30 Binary files /dev/null and b/intercompany_transaction_ept/static/description/screens/18(11).png differ diff --git a/intercompany_transaction_ept/static/description/screens/19(12).png b/intercompany_transaction_ept/static/description/screens/19(12).png new file mode 100644 index 0000000..e12d18d Binary files /dev/null and b/intercompany_transaction_ept/static/description/screens/19(12).png differ diff --git a/intercompany_transaction_ept/static/description/screens/2(2-2).png b/intercompany_transaction_ept/static/description/screens/2(2-2).png new file mode 100644 index 0000000..f9e38cb Binary files /dev/null and b/intercompany_transaction_ept/static/description/screens/2(2-2).png differ diff --git a/intercompany_transaction_ept/static/description/screens/20(13).png b/intercompany_transaction_ept/static/description/screens/20(13).png new file mode 100644 index 0000000..c3da030 Binary files /dev/null and b/intercompany_transaction_ept/static/description/screens/20(13).png differ diff --git a/intercompany_transaction_ept/static/description/screens/21(14).png b/intercompany_transaction_ept/static/description/screens/21(14).png new file mode 100644 index 0000000..78b558b Binary files /dev/null and b/intercompany_transaction_ept/static/description/screens/21(14).png differ diff --git a/intercompany_transaction_ept/static/description/screens/22(15).png b/intercompany_transaction_ept/static/description/screens/22(15).png new file mode 100644 index 0000000..23bb600 Binary files /dev/null and b/intercompany_transaction_ept/static/description/screens/22(15).png differ diff --git a/intercompany_transaction_ept/static/description/screens/23(16).png b/intercompany_transaction_ept/static/description/screens/23(16).png new file mode 100644 index 0000000..8d50b1b Binary files /dev/null and b/intercompany_transaction_ept/static/description/screens/23(16).png differ diff --git a/intercompany_transaction_ept/static/description/screens/24(17).png b/intercompany_transaction_ept/static/description/screens/24(17).png new file mode 100644 index 0000000..8888b60 Binary files /dev/null and b/intercompany_transaction_ept/static/description/screens/24(17).png differ diff --git a/intercompany_transaction_ept/static/description/screens/25(18).png b/intercompany_transaction_ept/static/description/screens/25(18).png new file mode 100644 index 0000000..f343cb4 Binary files /dev/null and b/intercompany_transaction_ept/static/description/screens/25(18).png differ diff --git a/intercompany_transaction_ept/static/description/screens/26(20).png b/intercompany_transaction_ept/static/description/screens/26(20).png new file mode 100644 index 0000000..1f3c45d Binary files /dev/null and b/intercompany_transaction_ept/static/description/screens/26(20).png differ diff --git a/intercompany_transaction_ept/static/description/screens/27(21).png b/intercompany_transaction_ept/static/description/screens/27(21).png new file mode 100644 index 0000000..b7c9f6d Binary files /dev/null and b/intercompany_transaction_ept/static/description/screens/27(21).png differ diff --git a/intercompany_transaction_ept/static/description/screens/28(22).png b/intercompany_transaction_ept/static/description/screens/28(22).png new file mode 100644 index 0000000..1ce937c Binary files /dev/null and b/intercompany_transaction_ept/static/description/screens/28(22).png differ diff --git a/intercompany_transaction_ept/static/description/screens/3 (3-3).png b/intercompany_transaction_ept/static/description/screens/3 (3-3).png new file mode 100644 index 0000000..9b98517 Binary files /dev/null and b/intercompany_transaction_ept/static/description/screens/3 (3-3).png differ diff --git a/intercompany_transaction_ept/static/description/screens/4(4-4).png b/intercompany_transaction_ept/static/description/screens/4(4-4).png new file mode 100644 index 0000000..7a08dd5 Binary files /dev/null and b/intercompany_transaction_ept/static/description/screens/4(4-4).png differ diff --git a/intercompany_transaction_ept/static/description/screens/5(19).png b/intercompany_transaction_ept/static/description/screens/5(19).png new file mode 100644 index 0000000..08fa7de Binary files /dev/null and b/intercompany_transaction_ept/static/description/screens/5(19).png differ diff --git a/intercompany_transaction_ept/static/description/screens/6(n).png b/intercompany_transaction_ept/static/description/screens/6(n).png new file mode 100644 index 0000000..def41d3 Binary files /dev/null and b/intercompany_transaction_ept/static/description/screens/6(n).png differ diff --git a/intercompany_transaction_ept/static/description/screens/7(5-5).png b/intercompany_transaction_ept/static/description/screens/7(5-5).png new file mode 100644 index 0000000..087c891 Binary files /dev/null and b/intercompany_transaction_ept/static/description/screens/7(5-5).png differ diff --git a/intercompany_transaction_ept/static/description/screens/8(1).png b/intercompany_transaction_ept/static/description/screens/8(1).png new file mode 100644 index 0000000..e9a74fa Binary files /dev/null and b/intercompany_transaction_ept/static/description/screens/8(1).png differ diff --git a/intercompany_transaction_ept/static/description/screens/9(2).png b/intercompany_transaction_ept/static/description/screens/9(2).png new file mode 100644 index 0000000..be8817a Binary files /dev/null and b/intercompany_transaction_ept/static/description/screens/9(2).png differ diff --git a/intercompany_transaction_ept/static/src/img/worldpay_icon.png b/intercompany_transaction_ept/static/src/img/worldpay_icon.png new file mode 100755 index 0000000..8453758 Binary files /dev/null and b/intercompany_transaction_ept/static/src/img/worldpay_icon.png differ diff --git a/intercompany_transaction_ept/static/src/js/payment_worldpay_ept.js b/intercompany_transaction_ept/static/src/js/payment_worldpay_ept.js new file mode 100755 index 0000000..cb673f1 --- /dev/null +++ b/intercompany_transaction_ept/static/src/js/payment_worldpay_ept.js @@ -0,0 +1,64 @@ +odoo.define('payment_worldpay_ept.worldpay', function(require) { + "use strict"; + + var ajax = require('web.ajax'); + var rpc = require('web.rpc'); + + function worldpay_payment_submit (clientkey) { + Worldpay.useTemplateForm({ + 'clientKey':clientkey, + 'form':'paymentFormworldpay', + 'paymentSection':'wk_woldpayPaymentSection', + 'display':'inline', + 'saveButton':false, + 'type':'card', + 'callback': function(obj) { + if (obj && obj.token) { + submit_payment_worldpay(obj.token) + } + else{ + alert('please try again'); + } + } + }); + } + + function submit_payment_worldpay(token) { + $('#ept_worldpay_myModal').modal('hide'); + var payment_form = $('.o_payment_form'); + payment_form.append('
') + var $form = $("input[name='worldpay_client_key']").parent('form'); + $form.attr('action','/payment/worldpay/feedback') + var $input = $('').val(token); + $form.append($input); + $form.submit(); + } + + require('web.dom_ready'); + if (!$('.o_payment_form').length) { + return $.Deferred().reject("DOM doesn't contain '.o_payment_form'"); + } + + $('#submit_ept_worldpay_payment').on('click',function(){ + Worldpay.submitTemplateForm(); + }); + + $.getScript("https://cdn.worldpay.com/v1/worldpay.js", function() { + $('#ept_worldpay_myModal').appendTo('body').modal('show'); + + //Bootstrap Model Close Event + $("#ept_worldpay_myModal").on('hidden.bs.modal', function () { + $("#ept_worldpay_myModal").remove(); //remove model + $('.modal-backdrop').remove(); + $("input[name='worldpay_client_key']").parent("form[provider='worldpay']").remove(); //remove Form + }); + + var worldpay_client_key = $("input[name='worldpay_client_key']").val(); + if(worldpay_client_key && worldpay_client_key != 'dummy'){ + worldpay_payment_submit(worldpay_client_key); + } + else{ + alert('please configure worldpay client key') + } + }); +}); diff --git a/intercompany_transaction_ept/views/all_search_views.xml b/intercompany_transaction_ept/views/all_search_views.xml new file mode 100644 index 0000000..d266b93 --- /dev/null +++ b/intercompany_transaction_ept/views/all_search_views.xml @@ -0,0 +1,124 @@ + + + + inter.company.transfer.ept.sale.view.search + sale.order + + + + + + + + + + + + inter.company.transfer.ept.purchase.view.search + purchase.order + + + + + + + + + + + + + inter.company.transfer.ept.invoice.view.search + account.move + + + + + + + + + + + + + + Inter Company Transfer Search View + inter.company.transfer.ept + search + + + + + + + + + + + + + + + + + + + Inter Company Transfer Log Search View + inter.company.transfer.log.ept + search + + + + + + + + + + + + + + + + + + inter.company.transfer.ept.purchase.view.search + stock.picking + + + + + + + + + + + + inter.company.transfer.ept.sale.order.view.search + sale.report + + + + + + + + + + inter.company.transfer.ept.sale.order.pivot.view.search + sale.report + + + + + + + + + + + diff --git a/intercompany_transaction_ept/views/inter_company_transfer_config_views.xml b/intercompany_transaction_ept/views/inter_company_transfer_config_views.xml new file mode 100644 index 0000000..d2e2d55 --- /dev/null +++ b/intercompany_transaction_ept/views/inter_company_transfer_config_views.xml @@ -0,0 +1,56 @@ + + + + + inter.company.transfer.config.ept.view.tree + inter.company.transfer.config.ept + tree + + + + + + + + + + + + inter.company.transfer.config.ept.view.form + inter.company.transfer.config.ept + form + +
+ + + + + + + + + + + + +
+
+
+ + + Configuration + ir.actions.act_window + inter.company.transfer.config.ept + + tree,form + + + + +
+
\ No newline at end of file diff --git a/intercompany_transaction_ept/views/inter_company_transfer_process_log_views.xml b/intercompany_transaction_ept/views/inter_company_transfer_process_log_views.xml new file mode 100644 index 0000000..0df2bce --- /dev/null +++ b/intercompany_transaction_ept/views/inter_company_transfer_process_log_views.xml @@ -0,0 +1,106 @@ + + + + + inter.company.transfer.process.log.view.tree + inter.company.transfer.log.ept + + + + + + + + + + + + inter.company.transfer.process.view.form + inter.company.transfer.log.ept + +
+ + + + + + + + + + + + + + + + + + + + + + +
+
+
+ + + + Operation Log + ir.actions.act_window + inter.company.transfer.log.ept + + tree,form + + + + + + + + + inter.company.transfer.process.log.line.view.tree + inter.company.transfer.log.line.ept + + + + + + + + + + + inter.company.transfer.process.log.line.view.form + inter.company.transfer.log.line.ept + +
+ + + + + + + + + + + +
+
+
+ + + + Operation Log Line + ir.actions.act_window + inter.company.transfer.log.line.ept + + tree,form + +
+
\ No newline at end of file diff --git a/intercompany_transaction_ept/views/inter_company_transfer_views.xml b/intercompany_transaction_ept/views/inter_company_transfer_views.xml new file mode 100644 index 0000000..8756498 --- /dev/null +++ b/intercompany_transaction_ept/views/inter_company_transfer_views.xml @@ -0,0 +1,204 @@ + + + + + inter.company.transfer.ept.view.tree + inter.company.transfer.ept + tree + + + + + + + + + + + + + + inter.company.transfer.ept.view.form + inter.company.transfer.ept + form + +
+
+
+ +
+ + + + + + + + + + + + + + + +
+ +
+

+ +

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ + + + Inter Company Transfer + ir.actions.act_window + inter.company.transfer.ept + form + [('type','=','ict')] + {'default_type':'ict','type':'ict'} + tree,form + + + + Internal Transfer + ir.actions.act_window + inter.company.transfer.ept + form + [('type','=','internal')] + {'default_type':'internal', 'type':'internal'} + + tree,form + + + + Reverse Transfer + ir.actions.act_window + inter.company.transfer.ept + form + [('type','=','ict_reverse')] + {'default_type':'ict_reverse','type':'ict_reverse'} + tree,form + + + + + + + + + + + + +
+
+ \ No newline at end of file diff --git a/intercompany_transaction_ept/views/purchase_views.xml b/intercompany_transaction_ept/views/purchase_views.xml new file mode 100644 index 0000000..ded6ce9 --- /dev/null +++ b/intercompany_transaction_ept/views/purchase_views.xml @@ -0,0 +1,18 @@ + + + + + inter.company.transfer.ept.purchase.view.form + purchase.order + + + + + + + + + + + + \ No newline at end of file diff --git a/intercompany_transaction_ept/views/rescompany_views.xml b/intercompany_transaction_ept/views/rescompany_views.xml new file mode 100644 index 0000000..026fb7e --- /dev/null +++ b/intercompany_transaction_ept/views/rescompany_views.xml @@ -0,0 +1,21 @@ + + + + + inter.company.transfer.ept.rescompany.view.form + res.company + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/intercompany_transaction_ept/views/sale_views.xml b/intercompany_transaction_ept/views/sale_views.xml new file mode 100644 index 0000000..8b6d6bd --- /dev/null +++ b/intercompany_transaction_ept/views/sale_views.xml @@ -0,0 +1,17 @@ + + + + + inter.company.transfer.ept.sale.view.form + sale.order + + + + + + + + + + + \ No newline at end of file diff --git a/intercompany_transaction_ept/views/stock_picking_views.xml b/intercompany_transaction_ept/views/stock_picking_views.xml new file mode 100644 index 0000000..7d060dd --- /dev/null +++ b/intercompany_transaction_ept/views/stock_picking_views.xml @@ -0,0 +1,18 @@ + + + + + inter.company.transfer.ept.pickings.view.form + stock.picking + + + + + + + + + + + + \ No newline at end of file diff --git a/intercompany_transaction_ept/wizard/__init__.py b/intercompany_transaction_ept/wizard/__init__.py new file mode 100644 index 0000000..8988883 --- /dev/null +++ b/intercompany_transaction_ept/wizard/__init__.py @@ -0,0 +1,2 @@ +from . import reverse_inter_company_transfer_ept +from . import import_export_product_list_ept \ No newline at end of file diff --git a/intercompany_transaction_ept/wizard/__pycache__/__init__.cpython-36.pyc b/intercompany_transaction_ept/wizard/__pycache__/__init__.cpython-36.pyc new file mode 100644 index 0000000..6e7e782 Binary files /dev/null and b/intercompany_transaction_ept/wizard/__pycache__/__init__.cpython-36.pyc differ diff --git a/intercompany_transaction_ept/wizard/__pycache__/import_export_product_list_ept.cpython-36.pyc b/intercompany_transaction_ept/wizard/__pycache__/import_export_product_list_ept.cpython-36.pyc new file mode 100644 index 0000000..d0c34f0 Binary files /dev/null and b/intercompany_transaction_ept/wizard/__pycache__/import_export_product_list_ept.cpython-36.pyc differ diff --git a/intercompany_transaction_ept/wizard/__pycache__/reverse_inter_company_transfer_ept.cpython-36.pyc b/intercompany_transaction_ept/wizard/__pycache__/reverse_inter_company_transfer_ept.cpython-36.pyc new file mode 100644 index 0000000..b0e1027 Binary files /dev/null and b/intercompany_transaction_ept/wizard/__pycache__/reverse_inter_company_transfer_ept.cpython-36.pyc differ diff --git a/intercompany_transaction_ept/wizard/import_export_product_list_ept.py b/intercompany_transaction_ept/wizard/import_export_product_list_ept.py new file mode 100644 index 0000000..c82810d --- /dev/null +++ b/intercompany_transaction_ept/wizard/import_export_product_list_ept.py @@ -0,0 +1,298 @@ +from odoo import models, fields, api, _ +from odoo.exceptions import ValidationError, Warning +import base64 +import csv +from io import StringIO, BytesIO +from csv import DictReader +from csv import DictWriter +from datetime import datetime +import logging +_logger = logging.getLogger(__name__) +import xlrd + +try: + import xlwt +except ImportError: + xlwt = None + + +class ImportExportProductList(models.TransientModel): + _name = "import.export.product.list.ept" + _description = 'Import Export Product Process' + + datas = fields.Binary('File') + report_type = fields.Selection(selection=[('csv', 'CSV'), ('xls', 'Xls')], string='Report Type', required=True) + choose_file = fields.Binary("Select File") + file_name = fields.Char(string='file name') + + file_delimiter = fields.Selection([(',', ',')], default="," , string="Delimiter", help="Select a delimiter to process CSV file.") + update_existing = fields.Boolean('Do you want to update already exist record ?', default=False) + replace_product_qty = fields.Boolean('Do you want to replace product quantity?' , help=""" + If you select this option then it will replace product quantity by csv quantity field data, it will not perform addition like 2 quantity is there in line and csv contain 3, + then it will replace 2 by 3, it won't be updated by 5.If you have not selected this option then it will increase (addition) line quantity with csv quantity field data like 2 quantity in line, + and csv have 3 quantity then it will update line with 5 quantity""") + + +#=========================================================================== +# Import Product List +#=========================================================================== + + def import_product_list(self): + inter_companytransfer_obj = self.env['inter.company.transfer.ept'] + inter_companytransfer_line_obj = self.env['inter.company.transfer.line.ept'] + product_obj = self.env['product.product'] + ict_log_obj = self.env['inter.company.transfer.log.ept'] + + self.validate_process()[0] + file_name = self.file_name + index = file_name.rfind('.') + flag = 0 + if index == -1: + flag = 1 + extension = file_name[index + 1:] + + if flag or extension not in ['csv', 'xls']: + raise Warning('Incorrect file format found..!' 'Please provide only .csv or .xls file formate to import data!!!') + + + inter_companytransfer_id = self._context.get('record', False) + inter_companytransfer_id = inter_companytransfer_obj.browse(inter_companytransfer_id) + if inter_companytransfer_id: + log_record = ict_log_obj.return_log_record(inter_companytransfer_id, operation_type="import") + if inter_companytransfer_id.state not in ['draft']: + raise Warning('Current Inter Company Transfer state is not Draft State') + if self.report_type == 'csv': + if not self.file_delimiter: + raise Warning('Unable to process..!' 'Please select File Delimiter...') + + self.write({'datas':self.choose_file}) + self._cr.commit() + import_file = BytesIO(base64.decodestring(self.datas)) + csvf = StringIO(import_file.read().decode()) + reader = csv.DictReader(csvf, delimiter=',') + for line in reader: + if not line.get('default_code'): + raise Warning('Unable to process..!' 'Please Provide Default Code of Product...') + if not line.get('default_code').strip(): + raise Warning('Unable to process..!' 'Please Provide Default Code of Product...') + default_code = line.get('default_code').strip() + if default_code != None: + product_id = product_obj.search([('default_code', '=', default_code), ('type', '=', 'product')], limit=1) + if not product_id: + msg = "Product either Product Default code is not match any Product , File Default code is %s " % (default_code) + log_record.post_log_line(msg, type='mismatch') + continue + qty = line.get('qty').strip() + if qty: + if qty == '0': + qty = 1 + else: + qty = qty + else: + qty = 1.0 + inter_companytransfer_line = inter_companytransfer_line_obj.search([('inter_transfer_id', '=', inter_companytransfer_id.id), ('product_id', '=', product_id.id)], limit=1) + if inter_companytransfer_line: + if self.update_existing: + if self.replace_product_qty: + if qty != '0': + inter_companytransfer_line.write({'quantity':float(qty)}) + else: + msg = "Inter Company Transfer Line remove due to File Qty is %s and Default Code %s and Product %s" % (qty, default_code, product_id.name) + log_record.post_log_line(msg, type='info') + inter_companytransfer_line.sudo().unlink() + else: + if qty != '0': + inter_companytransfer_line.write({'quantity':inter_companytransfer_line.quantity + float(qty)}) + else: + msg = "Inter Company Transfer Line remove due to File Qty is %s and Default Code %s and Product %s" % (qty, default_code, product_id.name) + log_record.post_log_line(msg, type='info') + inter_companytransfer_line.sudo().unlink() + if not inter_companytransfer_line: + vals = {'inter_transfer_id':inter_companytransfer_id.id, 'product_id':product_id.id, 'quantity':qty} + if line.get('price'): + price = line.get('price') + if price != None: + price = line.get('price') + if price != None: + vals.update({'price':price}) + else: + price = product_id.lst_price + vals.update({'price':price}) + inter_companytransfer_line_id = inter_companytransfer_line_obj.create(vals) + if not line.get('price'): + inter_companytransfer_line_id.default_price_get() + + elif self.report_type == 'xls': + try: + worksheet = self.read_file(self.choose_file) + file_header = self.get_xls_header(worksheet) + except Exception as e: + raise Warning("Something is wrong.\n %s" % (str(e))) + if self.validate_fields(file_header): + file_line_data = self.prepare_xls_data(worksheet, file_header) + for line_data in file_line_data: + default_code = line_data.get('default code', '') + if type(default_code) == float: + default_code = str(int(default_code)) + else: + default_code = default_code = line_data.get('default code', '') and str(line_data.get('default code', '')) + default_code = default_code and default_code.rstrip('0').rstrip('.') + if default_code != None: + product_id = product_obj.search([('default_code', '=', default_code)], limit=1) + if not product_id: + msg = "Product either Product Default code is not match any Product , File Default code is %s " % (default_code) + log_record.post_log_line(msg, type='mismatch') + continue + if type(line_data.get('qty')) in [None, str]: + quantity = 1.0 + else: + quantity = line_data.get('qty') + if quantity != None: + inter_companytransfer_line = inter_companytransfer_line_obj.search([('inter_transfer_id', '=', inter_companytransfer_id.id), ('product_id', '=', product_id.id)], limit=1) + if inter_companytransfer_line : + if self.update_existing: + if self.replace_product_qty: + if quantity != 0.0: + inter_companytransfer_line.write({'quantity':quantity}) + else: + msg = "Inter Company Transfer Line remove due to File Qty is %s and Default Code %s and Product %s" % (quantity, default_code, product_id.name) + log_record.post_log_line(msg, type='info') + inter_companytransfer_line.sudo().unlink() + else: + if quantity != 0.0: + inter_companytransfer_line.write({'quantity':inter_companytransfer_line.quantity + quantity}) + else: + msg = "Inter Company Transfer Line remove due to File Qty is %s and Default Code %s and Product %s" % (quantity, default_code, product_id.name) + log_record.post_log_line(msg, inter_companytransfer_id, type='info') + inter_companytransfer_line.sudo().unlink() + + if not inter_companytransfer_line: + if quantity != 0.0: + vals = {'inter_transfer_id':inter_companytransfer_id.id, 'product_id':product_id.id, 'quantity':quantity} + if line_data.get('price'): + price = line_data.get('price') + if price != None: + price = line_data.get('price') + if price != None: + vals.update({'price':price}) + else: + price = product_id.lst_price + vals.update({'price':price}) + inter_companytransfer_line_id = inter_companytransfer_line_obj.create(vals) + if not line_data.get('price'): + inter_companytransfer_line_id.default_price_get() + else: + msg = "File Qty is %s for this Product %s. So You can not Import Product Due to this Qty %s you can high your Qty" % (quantity, product_id.name, quantity) + log_record.post_log_line(msg, type='error') + + + if not log_record.ict_log_line_ids: + log_record.unlink() + return True + + + def validate_process(self): + if not self.choose_file: + raise Warning('Unable to process..!' 'Please select file to process...') + + return True + + + def read_file(self, choose_file): + try: + xl_workbook = xlrd.open_workbook(file_contents=base64.decodestring(choose_file)) + worksheet = xl_workbook.sheet_by_index(0) + except Exception as e: + raise e + return worksheet + + + + def get_xls_header(self, worksheet): + column_lst = [] + for col_index in range(worksheet.ncols): + column_lst.append(worksheet.cell(0, col_index).value.lower()) + return column_lst + + def validate_fields(self, file_fields): + require_fields = ['default code', 'qty'] + missing = [] + for field in require_fields: + if field not in file_fields: + missing.append(field) + + if len(missing) > 0: + raise Warning('Incorrect format found..! Please provide all the required fields in file, missing fields => %s.' % (missing)) + + return True + + + def prepare_xls_data(self, xl_sheet, keys): + value_list = [] + for row_index in range(1, xl_sheet.nrows): + valsdict = {keys[col_index]: xl_sheet.cell(row_index, col_index).value for col_index in range(xl_sheet.ncols)} + value_list.append(valsdict) + return value_list + +#=============================================================================== +# Export Product List +#=============================================================================== + + def export_product_list(self): + + inter_companytransfer_line_obj = self.env['inter.company.transfer.line.ept'] + line_ids = inter_companytransfer_line_obj.search([('inter_transfer_id', 'in', self.env.context.get('active_ids'))]) + inter_companytransfer_name = line_ids and line_ids[0].inter_transfer_id.name or '' + + if self.report_type == 'csv': + buffer = StringIO() + buffer.seek(0) + field_names = ['default_code', 'qty', 'price'] + csvwriter = DictWriter(buffer, field_names, delimiter=',') + csvwriter.writer.writerow(field_names) + + line_no = 0 + for line in line_ids: + data = {'default_code':line.product_id.default_code or "", + 'qty':line.quantity or 0, + 'price':line.price or 0} + line_no = line_no + 1 + csvwriter.writerow(data) + + buffer.seek(0) + file_data = buffer.read().encode() + file_data = base64.encodestring(file_data) + self.write({'datas':file_data}) + return {'type' : 'ir.actions.act_url', + 'url': 'web/content/?model=import.export.product.list.ept&download=true&field=datas&id=%s&filename=Export_Product_List_%s.csv' % (self.id, inter_companytransfer_name), + 'target': 'new', + } + + + elif self.report_type == 'xls': + workbook = xlwt.Workbook() + worksheet = workbook.add_sheet("Normal Sales Data", cell_overwrite_ok=True) + + worksheet.write(0, 0, 'Default Code') + worksheet.write(0, 1, 'Qty') + worksheet.write(0, 2, 'Price') + + row = 1 + for line in line_ids: + worksheet.write(row, 0, line.product_id.default_code or "") + worksheet.write(row, 1, line.quantity or 0) + worksheet.write(row, 2, line.price or 0) + row = row + 1 + + fp = BytesIO() + workbook.save(fp) + fp.seek(0) + report_data_file = base64.encodestring(fp.read()) + fp.close() + self.write({'datas':report_data_file}) + + return { + 'type' : 'ir.actions.act_url', + 'url': 'web/content/?model=import.export.product.list.ept&download=true&field=datas&id=%s&filename=Export_Product_List_%s.xls' % (self.id, inter_companytransfer_name), + 'target': 'new', + } diff --git a/intercompany_transaction_ept/wizard/import_export_product_list_views.xml b/intercompany_transaction_ept/wizard/import_export_product_list_views.xml new file mode 100644 index 0000000..9916ceb --- /dev/null +++ b/intercompany_transaction_ept/wizard/import_export_product_list_views.xml @@ -0,0 +1,52 @@ + + + + + import.export.product.list.view.form + import.export.product.list.ept + +
+ + + + + + + + + + + + + + + + + + + + + +
+
+
+
+
\ No newline at end of file diff --git a/intercompany_transaction_ept/wizard/reverse_inter_company_transfer_ept.py b/intercompany_transaction_ept/wizard/reverse_inter_company_transfer_ept.py new file mode 100644 index 0000000..ab22d0d --- /dev/null +++ b/intercompany_transaction_ept/wizard/reverse_inter_company_transfer_ept.py @@ -0,0 +1,50 @@ +from odoo import api, fields, models,_ +from odoo.exceptions import UserError, ValidationError +from odoo.exceptions import Warning + + + +class ReverseInterCompanyTransfer(models.TransientModel): + _name = 'reverse.inter.company.transfer.ept' + _description = 'Reverse Inter Company Process select lines' + + intercompany_transfer_id = fields.Many2one('inter.company.transfer.ept', string="ICT") + reverse_intercompanyline_ids = fields.One2many('reverse.inter.company.transfer.line.ept', 'reverse_intercompany_transfer_id', string="Reverse ICT Lines") + + + def action_create_reverse_process(self): + intercompany_transfer_obj = self.env['inter.company.transfer.ept'] + intercompany_transfer_line_obj = self.env['inter.company.transfer.line.ept'] + reverse_intercompany_transfer_id = self.intercompany_transfer_id.copy(default={'type':'ict_reverse', 'intercompany_transfer_id':self._context.get('active_id')}) + + product_lines = [] + for line in self.reverse_intercompanyline_ids: + intercompany_transfer_line = intercompany_transfer_line_obj.create({ 'inter_transfer_id':reverse_intercompany_transfer_id.id, 'product_id':line.product_id.id, + 'quantity':line.quantity or 1, 'price':line.price}) + + product_lines.append(intercompany_transfer_line.id) + + reverse_intercompany_transfer_id.write({'intercompany_transferline_ids':[(6, 0, product_lines)], 'intercompany_transfer_id':self.intercompany_transfer_id.id}) + return True + + +class ReverseInterCompanyTransferLines(models.TransientModel): + + _name = "reverse.inter.company.transfer.line.ept" + _description = "Reverse Transfer Lines" + + reverse_intercompany_transfer_id = fields.Many2one("reverse.inter.company.transfer.ept", string="Reverse ICT") + product_id = fields.Many2one('product.product', string="Product") + quantity = fields.Float("Quantity", default=1.0) + price = fields.Float('Price') + qty_delivered = fields.Float(string="Delivered Qty", store=True) + + + @api.constrains('quantity', 'qty_delivered') + def _check_quantity(self): + + if self.quantity > self.qty_delivered: + raise Warning(_('You can not enter quantity which was greater than original quantity')) + + + diff --git a/intercompany_transaction_ept/wizard/reverse_inter_company_transfer_views.xml b/intercompany_transaction_ept/wizard/reverse_inter_company_transfer_views.xml new file mode 100644 index 0000000..e9e1e9d --- /dev/null +++ b/intercompany_transaction_ept/wizard/reverse_inter_company_transfer_views.xml @@ -0,0 +1,38 @@ + + + + + reverse.inter.company.transfer.view.form + reverse.inter.company.transfer.ept + form + +
+ + + + + + + + + + + + + + + + + + + + +
+
+
+
+
\ No newline at end of file