From 7260f5dbc11fe0f1f0bb5afb05e8d4d59c7c023f Mon Sep 17 00:00:00 2001 From: Maxime Chambreuil Date: Thu, 16 Aug 2018 11:07:46 -0500 Subject: [PATCH 1/5] connector_spscommerce --- connector_spscommerce/README.rst | 21 + connector_spscommerce/__init__.py | 5 + connector_spscommerce/__openerp__.py | 28 + .../edi_scripts/connect_info.py | 9 + .../edi_scripts/csv_parser.py | 117 +++ connector_spscommerce/edi_scripts/edi_850.py | 642 ++++++++++++++ .../edi_scripts/edi_process_in.py | 793 ++++++++++++++++++ connector_spscommerce/edi_scripts/monitor.py | 149 ++++ connector_spscommerce/edi_scripts/test.py | 145 ++++ connector_spscommerce/models/__init__.py | 12 + .../models/account_invoice.py | 395 +++++++++ connector_spscommerce/models/edi_config.py | 81 ++ connector_spscommerce/models/procurement.py | 58 ++ connector_spscommerce/models/purchase.py | 12 + connector_spscommerce/models/sale.py | 633 ++++++++++++++ connector_spscommerce/models/stock.py | 657 +++++++++++++++ connector_spscommerce/models/stock_move.py | 61 ++ connector_spscommerce/readme/CONFIGURE.rst | 4 + connector_spscommerce/readme/CONTRIBUTORS.rst | 5 + connector_spscommerce/readme/CREDITS.rst | 3 + connector_spscommerce/readme/DESCRIPTION.rst | 11 + connector_spscommerce/readme/INSTALL.rst | 4 + connector_spscommerce/readme/USAGE.rst | 0 connector_spscommerce/static/src/img/icon.png | Bin 0 -> 4235 bytes .../views/account_invoice_view.xml | 75 ++ .../views/company_config_settings_view.xml | 132 +++ connector_spscommerce/views/sale_view.xml | 105 +++ connector_spscommerce/views/stock_view.xml | 147 ++++ 28 files changed, 4304 insertions(+) create mode 100644 connector_spscommerce/README.rst create mode 100644 connector_spscommerce/__init__.py create mode 100644 connector_spscommerce/__openerp__.py create mode 100644 connector_spscommerce/edi_scripts/connect_info.py create mode 100644 connector_spscommerce/edi_scripts/csv_parser.py create mode 100644 connector_spscommerce/edi_scripts/edi_850.py create mode 100644 connector_spscommerce/edi_scripts/edi_process_in.py create mode 100644 connector_spscommerce/edi_scripts/monitor.py create mode 100644 connector_spscommerce/edi_scripts/test.py create mode 100644 connector_spscommerce/models/__init__.py create mode 100644 connector_spscommerce/models/account_invoice.py create mode 100644 connector_spscommerce/models/edi_config.py create mode 100644 connector_spscommerce/models/procurement.py create mode 100644 connector_spscommerce/models/purchase.py create mode 100644 connector_spscommerce/models/sale.py create mode 100644 connector_spscommerce/models/stock.py create mode 100644 connector_spscommerce/models/stock_move.py create mode 100644 connector_spscommerce/readme/CONFIGURE.rst create mode 100644 connector_spscommerce/readme/CONTRIBUTORS.rst create mode 100644 connector_spscommerce/readme/CREDITS.rst create mode 100644 connector_spscommerce/readme/DESCRIPTION.rst create mode 100644 connector_spscommerce/readme/INSTALL.rst create mode 100644 connector_spscommerce/readme/USAGE.rst create mode 100644 connector_spscommerce/static/src/img/icon.png create mode 100644 connector_spscommerce/views/account_invoice_view.xml create mode 100644 connector_spscommerce/views/company_config_settings_view.xml create mode 100644 connector_spscommerce/views/sale_view.xml create mode 100644 connector_spscommerce/views/stock_view.xml diff --git a/connector_spscommerce/README.rst b/connector_spscommerce/README.rst new file mode 100644 index 0000000..abf312b --- /dev/null +++ b/connector_spscommerce/README.rst @@ -0,0 +1,21 @@ +**This file is going to be generated by oca-gen-addon-readme.** + +*Manual changes will be overwritten.* + +Please provide content in the ``readme`` directory: + +* **DESCRIPTION.rst** (required) +* INSTALL.rst (optional) +* CONFIGURE.rst (optional) +* **USAGE.rst** (optional, highly recommended) +* DEVELOP.rst (optional) +* ROADMAP.rst (optional) +* HISTORY.rst (optional, recommended) +* **CONTRIBUTORS.rst** (optional, highly recommended) +* CREDITS.rst (optional) + +Content of this README will also be drawn from the addon manifest, +from keys such as name, authors, maintainers, development_status, +and license. + +A good, one sentence summary in the manifest is also highly recommended. \ No newline at end of file diff --git a/connector_spscommerce/__init__.py b/connector_spscommerce/__init__.py new file mode 100644 index 0000000..be3a8a1 --- /dev/null +++ b/connector_spscommerce/__init__.py @@ -0,0 +1,5 @@ +# -*- coding: utf-8 -*- +# Copyright (c) Open Source Integrators +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from . import models diff --git a/connector_spscommerce/__openerp__.py b/connector_spscommerce/__openerp__.py new file mode 100644 index 0000000..b4b1e41 --- /dev/null +++ b/connector_spscommerce/__openerp__.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- +# Copyright (c) Open Source Integrators +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +{ + 'name': 'EDI Integration with SPS Commerce', + 'summary': 'Integrate with retail stores using SPS Commerce', + 'version': '9.0.1.0.0', + 'license': 'AGPL-3', + 'author': 'Open Source Integrators, Odoo Community Association (OCA)', + 'depends': [ + 'procurement', + 'account', + 'delivery', + 'sale_stock', + 'sale_automatic_workflow', + ], + 'data':[ + 'views/sale_view.xml', + 'views/stock_view.xml', + 'views/company_config_settings_view.xml', + 'views/account_invoice_view.xml', + ], + 'installable': True, + 'auto_install': False, + 'development_status': 'beta', + 'maintainers': ['smangukiya'], +} diff --git a/connector_spscommerce/edi_scripts/connect_info.py b/connector_spscommerce/edi_scripts/connect_info.py new file mode 100644 index 0000000..8aec630 --- /dev/null +++ b/connector_spscommerce/edi_scripts/connect_info.py @@ -0,0 +1,9 @@ +# Set the parameters appropriately before attempting EDI connection +# OERP Constants +USERNAME = 'admin' +PWD = 'admin' +DBNAME = 'Medico-Master-04032016' +ERP_WWW = "http://0.0.0.0:8069" +EOL_MARKER = '~' +DEBUG = True +IN_PATH = '/home/ram/Desktop/spscommercetask/Test/' diff --git a/connector_spscommerce/edi_scripts/csv_parser.py b/connector_spscommerce/edi_scripts/csv_parser.py new file mode 100644 index 0000000..346601e --- /dev/null +++ b/connector_spscommerce/edi_scripts/csv_parser.py @@ -0,0 +1,117 @@ +# -*- coding: utf-8 -*- +# Copyright (c) Open Source Integrators +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +import csv +import itertools + +CSV_PATH = '/tmp/csv_import/' +OUT_PATH = '/tmp/csv_export/' + + +def parse_csv(file): + + # CSV COLUMNS + cols = [ + 'partner', + 'po_num', + 'date', + 'ship_to_code', + 'sku', + 'upc', + 'product_desc', + 'product_qty', + 'product_uom', + 'price_unit', + 'third_party', + 'address1', + 'address2', + 'city', + 'state', + 'zip', + 'country', + 'billing_contact', + 'billing_address1', + 'billing_address2', + 'billing_city', + 'billing_state', + 'billing_zip', + 'billing_country', + 'ship_not_before', + 'cancel_after' + ] + + csv_obj = open(CSV_PATH + file, 'rb') + data = csv.reader(csv_obj, delimiter=',') + count = 1 + record = {} + record['sku'] = [] + record['upc'] = [] + + for col in itertools.islice(cols, 6, 9): + record[col] = [] + + for row in data: + + if count == 1 and row[0] != 'H': + + print """INFO: ***** FAILURE - YOUR FILE DOES NOT HAVE THE REQUIRED + FIRST ROW WITH COLUMN HEADINGS: + EDI Loop | Order line | Partner | PO # | Date/UPC | + code?/product desc | quantity | UOM Price ***** + """ + break + + if count > 2 and row[0] == '': + # sale_id, sale_name = process_record(sock, uid, record) + break + + if row[0] == 'H': + + if record['sku']: + + sale_id, sale_name = process_record(sock, uid, record) + record['sku'] = [] + record['upc'] = [] + + for col in itertools.islice(cols, 6, 9): + record[col] = [] + + record['ship_to_code'] = row[14] + record['ship_not_before'] = row[31] + record['cancel_after'] = row[32] + + # get partner, PO#, date + i = 2 + for col in itertools.islice(cols, 0, 3): + record[col] = row[i] + i += 1 + + # get thirdparty info + i = 7 + for col in itertools.islice(cols, 10, 16): + record[col] = row[i] + i += 1 + + # get billing address info + i = 15 + for col in itertools.islice(cols, 17, 24): + record[col] = row[i] + i += 1 + + elif row[0] == 'I': + + record['sku'].append(row[2]) + record['upc'].append(row[4]) + + # get sale line data: sku, asin, product description, quantity, + # uom, price + i = 6 + for col in itertools.islice(cols, 6, 9): + record[col].append(row[i]) + i += 1 + + count += 1 + + sale_id, sale_name = process_record(sock, uid, record) + + csv_obj.close() diff --git a/connector_spscommerce/edi_scripts/edi_850.py b/connector_spscommerce/edi_scripts/edi_850.py new file mode 100644 index 0000000..20ce563 --- /dev/null +++ b/connector_spscommerce/edi_scripts/edi_850.py @@ -0,0 +1,642 @@ +#!/usr/bin/python +# Copyright (c) Open Source Integrators +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +import sys +from datetime import datetime +from connect_info import DBNAME, PWD + + +def get_state(sock, uid, state): + args = [('code', '=', state)] + ids = sock.execute(DBNAME, uid, PWD, 'res.country.state', 'search', args) + + try: + state_id = ids[0] + return state_id + + except Exception: + "There are no states matching the code" + pass + + +def get_country(sock, uid, country): + args = [('code', '=', country)] + ids = sock.execute(DBNAME, uid, PWD, 'res.country', 'search', args) + + try: + country_id = ids[0] + return country_id + + except Exception: + "There are no states matching the code" + pass + + +def get_current_company(sock, uid): + + args = [('partner_id', '=', 1)] + ids = sock.execute(DBNAME, uid, PWD, 'res.company', 'search', args) + + try: + company_id = ids[0] + return company_id + + except Exception: + "There is no company with partner_id = 1" + pass + + +def edi_config_id( + sock, + uid, + company_id, + partner_header_string, + vendor_header_string): + + args = [('partner_header_string', '=', partner_header_string), + ('vendor_header_string', '=', vendor_header_string)] + ids = sock.execute(DBNAME, uid, PWD, 'edi.config', 'search', args) + edi = [] + + try: + edi = ids[0] + + except Exception: + print "There are no trading partners to process" + pass + + return edi + + +def get_config(sock, uid, edi): + + fields = [ + 'partner_header_string', + 'vendor_header_string', + 'salesperson', + 'edi_company_id', + 'in_path', + 'out_path', + 'log_path', + 'archive_path', + 'trading_partner_id', + 'ack_855', + 'auto_workflow'] + record = sock.execute(DBNAME, uid, PWD, 'edi.config', 'read', edi, fields) + try: + rec = record['trading_partner_id'] + rec = record['out_path'] + rec = record['archive_path'] + return record + except Exception: + print "There are no trading partners to process, paths not set or EOL" \ + " marker not defined." + pass + + +def get_partner_info(sock, uid, partner_id): + + fields = [ + 'is_company', + 'city', + 'country_id', + 'company_id', + 'email', + 'fax', + 'name', + 'phone', + 'state_id', + 'street', + 'street2', + 'website', + 'zip', + 'ship_to_code', + 'user_id', + 'property_product_pricelist'] + + try: + result = sock.execute( + DBNAME, uid, PWD, 'res.partner', 'read', partner_id, fields) + return result + + except Exception: + "There are no partners to process" + pass + + +def get_shipping_partner(sock, uid, partner_id, ship_to_code): + args = [('parent_id', '=', partner_id), + ('ship_to_code', '=', ship_to_code)] + ids = sock.execute(DBNAME, uid, PWD, 'res.partner', 'search', args) + shipping_id = False + try: + shipping_id = ids[0] + except Exception: + "There are no shipping partners to process" + pass + + return shipping_id + + +def get_billing_partner(sock, uid, partner_id): + args = [('parent_id', '=', partner_id), ('type', '=', 'invoice')] + ids = sock.execute(DBNAME, uid, PWD, 'res.partner', 'search', args) + billing_id = False + try: + billing_id = ids[0] + except Exception: + "There are no billing partners to process" + pass + + return billing_id + + +def search_uom(sock, uid, uom): + + uom_id = '' + + args = [('name', 'ilike', uom)] + ids = sock.execute(DBNAME, uid, PWD, 'product.uom', 'search', args) + try: + uom_id = ids[0] + except Exception: + pass + + return uom_id + + +def search_pmt_terms(sock, uid, payment_terms): + + pmt_id = '' + + args = [('name', 'ilike', payment_terms)] + ids = sock.execute( + DBNAME, uid, PWD, 'account.payment.term', 'search', args) + try: + pmt_id = ids[0] + except Exception: + pass + + return pmt_id + + +def search_incoterm(sock, uid, code): + + incoterm_id = '' + + args = [('code', 'ilike', code)] + ids = sock.execute(DBNAME, uid, PWD, 'stock.incoterms', 'search', args) + try: + incoterm_id = ids[0] + except Exception: + pass + + return incoterm_id + + +def get_sale_name(sock, uid, sale_id): + + fields = ['name'] + result = sock.execute(DBNAME, uid, PWD, 'sale.order', + 'read', sale_id, fields) + return result + + +def search_cust_id(sock, uid, partner_name): + + cust_id = '' + + args = [('name', 'ilike', partner_name)] + ids = sock.execute(DBNAME, uid, PWD, 'res.partner', 'search', args) + try: + cust_id = ids[0] + except Exception: + pass + return cust_id + + +def search_channel_id(sock, uid): + + channel_id = False + + args = [('code', '=', 'edi')] + ids = sock.execute(DBNAME, uid, PWD, 'sale.channel', 'search', args) + try: + channel_id = ids[0] + except Exception: + pass + return channel_id + + +def search_user_id(sock, uid, user_name): + + user_id = '' + + args = [('name', 'ilike', user_name)] + ids = sock.execute(DBNAME, uid, PWD, 'res.users', 'search', args) + try: + user_id = ids[0] + except Exception: + pass + return user_id + + +def get_shop_location(sock, uid, warehouse_id): + + src_location_id = sock.execute( + DBNAME, + uid, + PWD, + 'stock.warehouse', + 'read', + warehouse_id, + ['lot_stock_id']) + + print 'shop stock loc: ' + str(src_location_id) + + return src_location_id + + +def search_pricelist_id(sock, uid, pricelist): + + pricelist_id = '' + + args = [('name', '=', pricelist_id)] + ids = sock.execute(DBNAME, uid, PWD, 'product.pricelist', 'search', args) + try: + pricelist_id = ids[0] + except Exception: + pass + return pricelist_id + + +def rename_picking_pol(picking_policy): + + pick_pol = 'one' + if (picking_policy == 'Deliver each product when available'): + pick_pol = 'direct' + return pick_pol + + +def rename_order_pol(order_policy): + order_pol = '' + order_pol_1 = ['manual', 'picking', 'prepaid'] + order_pol_2 = ['On Demand', 'On Delivery Order', 'Before Delivery'] + i = 0 + for pol in order_pol_2: + if order_policy == pol: + order_pol = order_pol_1[i] + break + i += 1 + if order_pol: + return order_pol + else: + print "FAILURE - ORDER POLICY MUST BE: On Demand, On Delivery Order," \ + " OR Before Delivery" + + +def check_po_number(sock, uid, client_order_ref, partner_id): + + result = False + # If PO number is found in EDI + if client_order_ref: + + sale_ids = sock.execute(DBNAME, + uid, + PWD, + 'sale.order', + 'search', + [('partner_id', + '=', + partner_id), + ('state', + '!=', + 'cancel'), + ('client_order_ref', + '=', + client_order_ref)]) + + # if sale_ids from the above search, then po number is duplicated, warn + # user, and do not input the sale order + if sale_ids: + print "This is a duplicate PO and will not be pushed into the" \ + " system." + result = True + + return result + + +def create_sale_order( + sock, + uid, + date, + po_num, + partner_id, + partner_invoice_id, + partner_shipping_id, + third_party, + address1, + address2, + city, + state, + zip, + country, + ship_to_code, + trading_partner_id, + ack_bool, + automatic_workflow_id): + + partner_info = get_partner_info(sock, uid, partner_id) + + user_id = False + if partner_info['user_id']: + user_id = partner_info['user_id'][0] + + pricelist_id = False + if partner_info['property_product_pricelist']: + pricelist_id = partner_info['property_product_pricelist'][0] + + src_location_id = '' + + # based on partner's billing address, look up in partner record to find + # billing contact + if partner_id: + + if get_billing_partner(sock, uid, partner_id): + partner_invoice_id = get_billing_partner(sock, uid, partner_id) + + # based on ship_to_code, look up in partner record to find shipping contact + if ship_to_code: + + if get_shipping_partner(sock, uid, partner_id, ship_to_code): + partner_shipping_id = get_shipping_partner( + sock, uid, partner_id, ship_to_code) + sale_hash = { + 'incoterm': 14, # hardcoded to "Delivery at Place" + 'date_order': date, + 'client_order_ref': po_num, + 'origin': '', + 'note': '', + 'user_id': user_id, + 'partner_id': partner_id, + # 'carrier_id': carrier_id, + 'pricelist_id': pricelist_id, + # 'payment_term': payment_term, + 'company_id': 1, + 'order_policy': 'picking', # hardcoded to "On Delivery" + 'partner_invoice_id': partner_invoice_id, + 'partner_shipping_id': partner_shipping_id, + 'edi_yes': 'TRUE', + 'ack_yes': ack_bool, + 'ack_sent': 'FALSE', + 'ship_to_code': ship_to_code, + 'trading_partner_id': trading_partner_id, + 'auto_workflow': automatic_workflow_id[0], + 'edi_est_so_ship_date': datetime.now().strftime('%Y-%m-%d') + } + + print "Attempting to create %s" % sale_hash + sale_id = sock.execute(DBNAME, uid, PWD, 'sale.order', 'create', sale_hash) + return sale_id, src_location_id, pricelist_id + + +def search_product(sock, uid, product_upc, product_sku): + + args = [('default_code', '=', product_sku)] + ids = sock.execute(DBNAME, uid, PWD, 'product.product', 'search', args) + try: + product_id = ids[0] + except Exception: + product_id = 'no_exist' + print 'product does not exist' + + return product_id + + +def get_product(sock, uid, product_id): + + fields = ['available_sale', 'uom_id', + 'weight', 'standard_price', 'default_code'] + record = sock.execute( + DBNAME, uid, PWD, 'product.product', 'read', product_id, fields) + try: + return record + except Exception: + pass + + +def rename_order_type(order_type): + + order_type_1 = ['from stock', 'on order'] + order_type_2 = ['make_to_stock', 'make_to_order'] + i = 0 + for otype in order_type_1: + if order_type == otype: + order_type = order_type_2[i] + break + i += 1 + return order_type + + +def create_so_lines( + sock, + uid, + sale_id, + product_upcs, + product_skus, + product_descs, + qtys, + uoms, + prices, + src_location_id, + ack_bool, + po_lines, + config_rec, + pricelist_id): + + sale_line_ids = [] + p_ids = [] + products_not_found = '' + + product_codes = product_upcs + if product_skus: + product_codes = product_skus + + for x in xrange(len(product_codes)): + + qty = qtys[x] + uom = uoms[x] + # price = prices[x] + uom_id = search_uom(sock, uid, uom) + product_code = product_codes[x] + product_sku = product_skus[x] + product_upc = product_upcs[x] + po_line = po_lines[x] + p_id = search_product(sock, uid, product_upc, product_sku) + partner_id = config_rec['trading_partner_id'][0] + + if p_id is not 'no_exist': + + price = sock.execute( + DBNAME, + uid, + PWD, + 'product.pricelist', + 'price_get_wrapper', + pricelist_id, + p_id, + qty, + partner_id) + product_info = get_product(sock, uid, p_id) + uom_id = product_info['uom_id'] + weight = product_info['weight'] + p_ids.append(p_id) + + order_hash = { + 'name': product_info['default_code'], + 'edi_line_num': po_line, + 'product_uos_qty': int(qty), + 'product_uom_qty': int(qty), + 'edi_line_qty': int(qty), + 'state': 'draft', + 'order_id': sale_id, # from sale order we just created + 'invoiced': False, + 'th_weight': weight, # lookup from product + 'product_id': p_id, + 'edi_yes': 'TRUE', + 'ack_yes': ack_bool, + 'price_unit': price, # passed in from PO + # lookup from product + 'purchase_price': product_info['standard_price'], + 'product_uom': uom_id[0], # lookup from product + } + + try: + line_id = sock.execute( + DBNAME, uid, PWD, 'sale.order.line', 'create', order_hash) + sale_line_ids.append(line_id) + + except Exception: + print "Could not add the order line", sys.exc_info()[0] + + else: + + print "INFO: ***** FAILURE TO FIND PRODUCT WITH UPC CODE: " + \ + str(product_code) + products_not_found = products_not_found + '\n Line not input: ' + \ + qty + ' ' + uom + ' of ' + product_code + + if products_not_found: + sock.execute(DBNAME, uid, PWD, 'sale.order', 'write', + sale_id, {'edi_error': products_not_found}) + + return p_ids, sale_line_ids + + +def process_record(sock, uid, record, partner_rec, config_rec): + + ack_bool = config_rec['ack_855'] + + # We're using fullfillment_channel to store Sale Automatic Workflow ID per + # customer, this is attached to the order + print "Automatic workflow is: " + str(config_rec['auto_workflow']) + automatic_workflow_id = config_rec['auto_workflow'] + # use the 850 PO info to create a sales order + sale_id, src_location_id, pricelist_id =\ + create_sale_order(sock, uid, + record['date'], + record['po_num'], + record['partner_id'], + record['partner_invoice_id'], + record['partner_shipping_id'], + record['third_party'], + record['address1'], + record['address2'], + record['city'], + record['state'], + record['zip'], + record['country'], + record['ship_to_code'], + config_rec['id'], + ack_bool, + automatic_workflow_id) + + # get the sale name from the order you just created + sale_name = get_sale_name(sock, uid, sale_id) + + # create SO LINE from dictionary we just created + product_ids, sale_line_ids =\ + create_so_lines(sock, uid, sale_id, + record['upc'], + record['sku'], + record['product_desc'], + record['product_qty'], + record['product_uom'], + record['price_unit'], + src_location_id, + ack_bool, + record['po_line'], + config_rec, + pricelist_id) + + return sale_id, sale_name, product_ids + + +def main(sock, uid, order_dict, in_path): + + sale_id = False + company_id = False + + if not company_id: + company_id = get_current_company(sock, uid) + + # get edi_config_id from company record + edi_id = edi_config_id(sock, uid, company_id, + order_dict['partner'], order_dict['vendor']) + # print 'EDI: ' + str(edi_id) + + # get configuration settings from trading_partner + + if edi_id: + + config = get_config(sock, uid, edi_id) + + # get edi trading partner info + partner_rec = get_partner_info( + sock, uid, config['trading_partner_id'][0]) + order_dict['partner_id'] = partner_rec['id'] + order_dict['partner_invoice_id'] = partner_rec['id'] + order_dict['partner_shipping_id'] = partner_rec['id'] + + # process record and input sale order with multiple order lines in + # OpenERP + + print "Processing PO#: " + str(order_dict['po_num']) + if not check_po_number( + sock, + uid, + order_dict['po_num'], + order_dict['partner_id']): + + try: + sale_id, sale_name, product_ids = process_record( + sock, uid, order_dict, partner_rec, config) + + except Exception: + print "Cannot process order. Sale order or order lines not" \ + " created. ", sys.exc_info() + + print "Sale added id: " + str(sale_id) + else: + print 'There is no trading partner that matches the partner header' \ + ' string in the 850 file.' + print 'Please verify that there are trading partners defined and that' \ + ' the header strings are correctly defined on them.' + + return sale_id + + +if __name__ == '__main__': + print 'Process: EDI In - Starting' + main() + print 'Process: EDI In - Ending' diff --git a/connector_spscommerce/edi_scripts/edi_process_in.py b/connector_spscommerce/edi_scripts/edi_process_in.py new file mode 100644 index 0000000..467e876 --- /dev/null +++ b/connector_spscommerce/edi_scripts/edi_process_in.py @@ -0,0 +1,793 @@ +#!/usr/bin/python +# Copyright (c) Open Source Integrators +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +import os +import xmlrpclib +import edi_850 +import shutil +import xmltodict +from datetime import datetime +from connect_info import ERP_WWW, DBNAME, USERNAME, PWD, DEBUG, IN_PATH + + +test_me = """ + + + + testVersion + +
+ + 000ALLTESTID + 1010101010101 + 00 + OS + This is a test. Do not ship + XYZ999999 + 2013-08-29 + 21:05:49-06:00 + Y + testBuyersCurrency + 026 + 99999999 + Sam's Club + 55555555 + + + 09 + 8 + 5.62 + 2013-11-28 + 60 + 2013-11-15 + 90 + 1845.08 + Must be paid in full by end of year + AB + 999888777 + + + 043 + 2012-10-31 + 16:13:03-05:00 + + + IC + SPS Commerce + 866-245-8100 + 612-435-9401 + info@spscommerce.com + +
+ FW + 9 + 11111 + SPS Commerce + ATTN: Marketing + 333 South 7th Street + ATTN: The President + Washington + MN + 55402 + USA + + BI + SPS Commerce + 866-245-8100 + 612-435-9401 + info@spscommerce.com + +
+ + RS + MI + Minnesota + + + SB + EMSY + ONTRAC + + + GD + testReferenceID + New products only. Do not reuse packaging + + + GEN + REPEAT LOGO PREVIOUS ORDER + + + EV + 1845.08 + 8.50 + CC + 07034 + 0 + 99990000 + + + A + F330 + 85.02 + 1 + 5.0 + 02 + This will cover the cost of shipping + +
+ + + + 01 + 9999-SPS + 11155-559999 + 093597609541 + 1234567890123 + 12345678901234 + 98765432101 + 51456-299 + 999-0-555-22222-0 + + VC + ABC-456789 + + 125 + DA + 5.85 + testBuyersCurrency + S-800 + Small + C-999 + Fire Truck Red + Faux Fabric + 026 + testClass + + 600 + 42-10651 + + + + 069 + 2012-10-31 + 16:13:03-05:00 + + + RPC + 5.48 + + + RE + Super comfortable + + + CP + 6 + 18 + BO + + + BY + testReferenceID + New products only. Do not reuse packaging + + + CAFA3 + REPEAT LOGO PREVIOUS ORDER + + + N + S + Hanger with clips + A-112233 + + + + + 02 + 9999-SPS + 11155-559999 + 093597609541 + 1234567890123 + 12345678901234 + 98765432101 + 51456-299 + 999-0-555-22222-0 + + N5 + ABC-456789 + + S-800 + Small + C-999 + Fire Truck Red + Faux Fabric + 5 + PP + 5.85 + + 600 + 42-10651 + + + + RTL + 5.48 + + + 74 + Super comfortable + + + Y + H + Hanger with clips + A-112233 + + + + + 100 + P9 + + 097 + 2013-12-21 + 16:13:03-05:00 + + + + CA + 1845.08 + 8.50 + CC + 07034 + 2 + 99990000 + + + C + D550 + 85.02 + 1 + 5.0 + 01 + This will cover the cost of shipping + + + + + 4058.92 + 3 + 90 + +
+
""" + + +def connect_oerp(): + ''' + Function that will make a connection to OpenERP using XML-RPC. + @return1: socket that is connected to OpenERP + @return2: An id that shows that you have been validated + ''' + sock = xmlrpclib.ServerProxy(ERP_WWW + '/xmlrpc/object') + sock_common = xmlrpclib.ServerProxy(ERP_WWW + '/xmlrpc/common') + uid = sock_common.login(DBNAME, USERNAME, PWD) + + print "XMLRPC Connection: SUCCESS - SERVER HAS AUTHENTICATED A LOGIN" + + return sock, uid + + +def parse_csv(sock, uid, file): + + # note xmltodict.unparse(file) will convert it back to xml. It will also + # convert any dict to xml. + orders = xmltodict.parse(test_me) +# order_dict = xmltodict.parse(test_me) + record = {} + + # improve process in + for order in orders['Orders']: + record['address_type_code'] = order['Order']['Header']['Address']['AddressTypeCode'] + record['location_code_qualifier'] = order['Order']['Header']['Address']['LocationCodeQualifier'] + record['address_location_number'] = order['Order']['Header']['Address']['AddressLocationNumber'] + record['address_name'] = order['Order']['Header']['Address']['AddressName'] + record['address_alternate_name'] = order['Order']['Header']['Address']['AddressAlternateName'] + record['address1'] = order['Order']['Header']['Address']['Address1'] + record['address2'] = order['Order']['Header']['Address']['Address2'] + record['city'] = order['Order']['Header']['Address']['City'] + record['state'] = order['Order']['Header']['Address']['State'] + record['postal_code'] = order['Order']['Header']['Address']['PostalCode'] + record['country'] = order['Order']['Header']['Address']['Country'] + record['contact'] = {} + record['contact']['contact_type_code'] = order['Order']['Header']['Address']['Contact']['ContactTypeCode'] + record['contact']['contact_name'] = order['Order']['Header']['Address']['Contact']['ContactName'] + record['contact']['primary_phone'] = order['Order']['Header']['Address']['Contact']['PrimaryPhone'] + record['contact']['primary_fax'] = order['Order']['Header']['Address']['Contact']['PrimaryFax'] + record['contact']['primary_email'] = order['Order']['Header']['Address']['Contact']['PrimaryEmail'] + record['date'] = order['Order']['Header']['Date']['Date1'] + record['time'] = order['Order']['Header']['Date']['Time1'] + record['edi_error'] = False + record['edi_yes'] = False + record['ack_yes'] = True + record['855_replace'] = True + record['supplier_code'] = '123' + record['ship_not_before_date'] = datetime.now().strftime('%Y-%m-%d') + record['cancel_after_date'] = datetime.now().strftime('%Y-%m-%d') + record['trading_partner_id'] = order['Order']['Header']['OrderHeader']['TradingPartnerId'] + record['scac_code'] = '' + record['bol_num'] = '' + record['asn_shipment'] = '' + record['tracking_num'] = '' + record['tset_purpose_code'] = order['Order']['Header']['OrderHeader']['TsetPurposeCode'] + record['purchase_order_number'] = order['Order']['Header']['OrderHeader']['PurchaseOrderNumber'] + record['purchase_order_type_code'] = order['Order']['Header']['OrderHeader']['PurchaseOrderTypeCode'] + record['po_type_description'] = order['Order']['Header']['OrderHeader']['POTypeDescription'] + record['ship_complete_code'] = order['Order']['Header']['OrderHeader']['ShipCompleteCode'] + record['department'] = order['Order']['Header']['OrderHeader']['Department'] + record['division'] = order['Order']['Header']['OrderHeader']['Division'] + record['promotion_deal_number'] = order['Order']['Header']['OrderHeader']['PromotionDealNumber'] + record['terms_type'] = order['Order']['Header']['PaymentTerms']['TermsType'] + record['terms_basis_date_code'] = order['Order']['Header']['PaymentTerms']['TermsBasisDateCode'] + record['terms_discount_percentage'] = order['Order']['Header']['PaymentTerms']['TermsDiscountPercentage'] + record['terms_discount_due_days'] = order['Order']['Header']['PaymentTerms']['TermsDiscountDueDays'] + record['terms_net_due_days'] = order['Order']['Header']['PaymentTerms']['TermsNetDueDays'] + record['payment_method_code'] = order['Order']['Header']['PaymentTerms']['PaymentMethodCode'] + record['fob_pay_code'] = order['Order']['Header']['FOBRelatedInstruction']['FOBPayCode'] + record['fob_location_qualifier'] = order['Order']['Header']['FOBRelatedInstruction']['FOBLocationQualifier'] + record['fob_location_description'] = order['Order']['Header']['FOBRelatedInstruction']['FOBLocationDescription'] + record['fob_title_passage_code'] = '' + record['fob_title_passage_location'] = '' + record['carrier_trans_method_code'] = order['Order']['Header']['CarrierInformation']['CarrierTransMethodCode'] + record['carrier_alpha_code'] = order['Order']['Header']['CarrierInformation']['CarrierAlphaCode'] + record['carrier_routing'] = order['Order']['Header']['CarrierInformation']['CarrierRouting'] + record['routing_sequence_code'] = '' + record['service_level_code'] = '' + record['reference_qual'] = order['Order']['Header']['Reference']['ReferenceQual'] + record['reference_id'] = order['Order']['Header']['Reference']['ReferenceID'] + record['ref_description'] = order['Order']['Header']['Reference']['Description'] + record['note_code'] = order['Order']['Header']['Notes']['NoteCode'] + record['note_information_field'] = order['Order']['Header']['Notes']['NoteInformationField'] + record['allow_chrg_indicator'] = order['Order']['Header']['ChargesAllowances']['AllowChrgIndicator'] + record['allow_chrg_code'] = order['Order']['Header']['ChargesAllowances']['AllowChrgCode'] + record['allow_chrg_agency_code'] = '' + record['allow_chrg_agency'] = '' + record['allow_chrg_amt'] = order['Order']['Header']['ChargesAllowances']['AllowChrgAmt'] + record['allow_chrg_percent_qual'] = order['Order']['Header']['ChargesAllowances']['AllowChrgPercentQual'] + record['allow_chrg_percent'] = order['Order']['Header']['ChargesAllowances']['AllowChrgPercent'] + record['allow_chrg_handling_code'] = order['Order']['Header']['ChargesAllowances']['AllowChrgHandlingCode'] + record['reference_identification'] = '' + record['allow_chrg_handling_description'] = order['Order']['Header']['ChargesAllowances']['AllowChrgHandlingDescription'] + + record['order_line'] = {} + for line in orders['Orders']['Order']['LineItems']: + order_line_data = { + 'product_qty': orders['Orders']['Order']['LineItems'][line]['OrderLine']['OrderQty'], + 'product_uom': orders['Orders']['Order']['LineItems'][line]['OrderLine']['OrderQtyUOM'], + 'sku': orders['Orders']['Order']['LineItems'][line]['OrderLine']['VendorPartNumber'], + 'edi_yes': False, + 'asn_shipment': '123', + 'po_number': '123', + 'buyer_part_number': orders['Orders']['Order']['LineItems'][line]['OrderLine']['BuyerPartNumber'], + 'edi_line_num': orders['Orders']['Order']['LineItems'][line]['OrderLine']['LineSequenceNumber'], + 'edi_line_qty': '123', + 'ack_yes': '123', + 'edi_est_del_date': datetime.now().strftime('%Y-%m-%d'), + 'edi_est_ship_date': datetime.now().strftime('%Y-%m-%d'), + 'edi_line_msg': '123', + 'ship_not_before_date': datetime.now().strftime('%Y-%m-%d'), + 'cancel_after_date': datetime.now().strftime('%Y-%m-%d'), + 'trading_partner_id': orders['Orders']['Order']['Header']['OrderHeader']['TradingPartnerId'], + 'edi_intransit_qty': 0, + 'edi_outgoing_qty': 0, + 'edi_avlforsale_qty': 0, + 'vendor_part_number': orders['Orders']['Order']['LineItems'][line]['OrderLine']['VendorPartNumber'], + 'consumer_package_code': orders['Orders']['Order']['LineItems'][line]['OrderLine']['ConsumerPackageCode'], + 'gtin': orders['Orders']['Order']['LineItems'][line]['OrderLine']['GTIN'], + 'upc_case_code': orders['Orders']['Order']['LineItems'][line]['OrderLine']['UPCCaseCode'], + 'purchase_price_basis': orders['Orders']['Order']['LineItems'][line]['OrderLine']['PurchasePrice'], + 'product_size_code': orders['Orders']['Order']['LineItems'][line]['OrderLine']['ProductSizeCode'], + 'product_size_description': orders['Orders']['Order']['LineItems'][line]['OrderLine']['ProductSizeDescription'], + 'product_color_code': orders['Orders']['Order']['LineItems'][line]['OrderLine']['ProductColorCode'], + 'product_color_description': orders['Orders']['Order']['LineItems'][line]['OrderLine']['ProductColorDescription'], + # 'product_material_code':orders['Orders']['Order']['LineItems'][line]['OrderLine']['ProductMaterialCode'], + 'product_material_description': orders['Orders']['Order']['LineItems'][line]['OrderLine']['ProductMaterialDescription'], + 'department': orders['Orders']['Order']['LineItems'][line]['OrderLine']['Department'], + 'classs': orders['Orders']['Order']['LineItems'][line]['OrderLine']['Class'], + 'price_type_id_code': orders['Orders']['Order']['LineItems'][line]['PriceInformation']['PriceTypeIDCode'], + 'edi_line_num': orders['Orders']['Order']['LineItems'][line]['OrderLine']['LineSequenceNumber'], + # 'multiple_price_quantity':orders['Orders']['Order']['LineItems'][line]['PriceInformation']['MultiplePriceQuantity'], + # 'class_of_trade_code':orders['Orders']['Order']['LineItems'][line]['PriceInformation']['ClassOfTradeCode'], + 'item_description_type': orders['Orders']['Order']['LineItems'][line]['ProductOrItemDescription']['ItemDescriptionType'], + 'product_characteristic_code': '', + 'agency_qualifier_code': '', + 'product_description_code': '', + 'pack_qualifier': orders['Orders']['Order']['LineItems'][line]['PhysicalDetails']['PackQualifier'], + 'pack_value': orders['Orders']['Order']['LineItems'][line]['PhysicalDetails']['PackValue'], + 'pack_size': orders['Orders']['Order']['LineItems'][line]['PhysicalDetails']['PackSize'], + 'pack_uom': orders['Orders']['Order']['LineItems'][line]['PhysicalDetails']['PackUOM'], + 'packing_medium': '', + 'packing_material': '', + 'pack_weight': 0, + 'pack_weight_uom': '', + 'location_code_qualifier': '', + 'location': '', + 'allow_chrg_indicator': orders['Orders']['Order']['LineItems'][line]['ChargesAllowances']['AllowChrgIndicator'], + 'allow_chrg_code': orders['Orders']['Order']['LineItems'][line]['ChargesAllowances']['AllowChrgCode'], + 'allow_chrg_agency_code': '', + 'allow_chrg_agency': '', + 'allow_chrg_amt': orders['Orders']['Order']['LineItems'][line]['ChargesAllowances']['AllowChrgAmt'], + 'allow_chrg_percent': orders['Orders']['Order']['LineItems'][line]['ChargesAllowances']['AllowChrgPercent'], + 'percent_dollar_basis': 0, + 'allow_chrg_rate': 0, + 'allow_chrg_handling_code': orders['Orders']['Order']['LineItems'][line]['ChargesAllowances']['AllowChrgHandlingCode'], + 'allow_chrg_qty_uom': '', + # 'allow_chrg_handling_description':orders['Orders']['Order']['LineItems'][line]['ChargesAllowances']['allow_chrg_handling_description'], + } + record['order_line'].update(order_line_data) + print "Stop", record + sale_id = create_sale_order(sock, uid, record) + print "Successfully created ORDER IMPORTED!!!!", sale_id + line_ids = create_so_lines(sock, uid, sale_id, record['order_line']) + print "Successfully created ORDER LINES IMPORTED!!!!", line_ids + return parse_csv + + +def get_order_header(order_header): + """Inputs: + order_header = " + + 000ALLTESTID + 1010101010101 + 00 + OS + This is a test. Do not ship + XYZ999999 + 2013-08-29 + 21:05:49-06:00 + Y + testBuyersCurrency + 026 + 99999999 + Sam's Club + 55555555 + " + Output: dictionary with values for header to be used in sale order + import + """ + return\ + order_header['Department'],\ + order_header['PurchaseOrderNumber'],\ + order_header['TradingPartnerId'],\ + order_header['PurchaseOrderDate'] + + +def get_state(sock, uid, state): + args = [('name', '=', state)] + ids = sock.execute(DBNAME, uid, PWD, 'res.country.state', 'search', args) + + try: + state_id = ids[0] + return state_id + + except Exception: + "There are no states matching the code" + return False + + +def get_country(sock, uid, country): + args = [('name', '=', country)] + ids = sock.execute(DBNAME, uid, PWD, 'res.country', 'search', args) + + try: + country_id = ids[0] + return country_id + + except Exception: + "There are no states matching the code" + return False + + +def get_partner_info( + sock, + uid, + partner_contact, + address1='', + address2='', + city='', + state='', + postal_code='', + country=''): + + fields = [ + 'is_company', + 'city', + 'country_id', + 'company_id', + 'email', + 'fax', + 'name', + 'phone', + 'state_id', + 'street', + 'street2', + 'website', + 'zip', + 'ship_to_code', + 'user_id', + 'property_product_pricelist'] + + try: + # check for existing partner if not then create + print "LOOK FOR THIS", partner_contact['contact_name'] + args = [('name', '=', partner_contact['contact_name'])] + ids = sock.execute(DBNAME, uid, PWD, 'res.partner', 'search', args) + if ids: + result = sock.execute( + DBNAME, uid, PWD, 'res.partner', 'read', ids[0], fields) + else: + partner_data = { + 'name': partner_contact['contact_name'], + 'phone': partner_contact['primary_phone'], + 'email': partner_contact['primary_email'], + 'fax': partner_contact['primary_fax'], + 'street': address1, + 'street2': address2, + 'city': city, + 'zip': postal_code, + 'state_id': get_state(sock, uid, state), + 'country_id': get_country(sock, uid, country), + 'user_id': uid, + } + partner_id = sock.execute( + DBNAME, uid, PWD, 'res.partner', 'create', partner_data) + result = sock.execute( + DBNAME, uid, PWD, 'res.partner', 'read', partner_id, fields) + return result + + except Exception: + "There are no partners to process" + pass + + +def search_product(sock, uid, product_sku): + + args = [('default_code', '=', product_sku)] + ids = sock.execute(DBNAME, uid, PWD, 'product.product', 'search', args) + try: + product_id = ids[0] + except Exception: + product_id = 'no_exist' + print 'product does not exist' + + return product_id + + +def create_sale_order(sock, uid, order_data): + partner_info = get_partner_info( + sock, + uid, + order_data['contact'], + order_data['address1'], + order_data['address2'], + order_data['city'], + order_data['state'], + order_data['postal_code'], + order_data['country']) + user_id = False + if partner_info['user_id']: + user_id = partner_info['user_id'][0] + + pricelist_id = False + if partner_info['property_product_pricelist']: + pricelist_id = partner_info['property_product_pricelist'][0] + + # based on partner's billing address, look up in partner record to find + # billing contact + if partner_info['id']: + partner_invoice_id = partner_info['id'] + partner_shipping_id = partner_info['id'] + print "datetime.now().strftime('%Y-%m-%d %H:%M:%S')",\ + datetime.now().strftime('%Y-%m-%d %H:%M:%S') + sale_hash = { + 'incoterm': 14, # hardcoded to "Delivery at Place" + 'date_order': datetime.now().strftime('%Y-%m-%d %H:%M:%S'), + 'client_order_ref': order_data['purchase_order_number'], + 'origin': False, + 'note': '', + 'user_id': user_id or 1, + 'team_id': 1, + 'warehouse_id': 9, + 'partner_id': partner_info['id'] or False, + # 'carrier_id': carrier_id, + 'pricelist_id': pricelist_id or 1, + 'project_id': 1, + 'currency_id': 1, + # 'payment_term': payment_term, + 'company_id': 1, + 'order_policy': 'picking', # hardcoded to "On Delivery", + 'picking_policy': 'direct', + 'partner_invoice_id': partner_invoice_id, + 'partner_shipping_id': partner_shipping_id, + 'edi_yes': True, + 'ack_yes': True, + 'ack_sent': False, + 'ship_to_code': partner_info['ship_to_code'], + 'trading_partner_id': partner_info['id'], + 'edi_est_so_ship_date': datetime.now().strftime('%Y-%m-%d'), + 'tset_purpose_code': order_data['tset_purpose_code'], + 'purchase_order_type_code': order_data['purchase_order_type_code'], + 'po_type_description': order_data['po_type_description'], + 'ship_complete_code': order_data['ship_complete_code'], + 'department': order_data['department'], + 'division': order_data['division'], + 'promotion_deal_number': order_data['promotion_deal_number'], + 'terms_type': order_data['terms_type'], + 'terms_basis_date_code': order_data['terms_basis_date_code'], + 'terms_discount_percentage': order_data['terms_discount_percentage'], + 'terms_discount_due_days': order_data['terms_discount_due_days'], + 'terms_net_due_days': order_data['terms_net_due_days'], + 'payment_method_code': order_data['payment_method_code'], + 'fob_pay_code': order_data['fob_pay_code'], + 'fob_location_qualifier': order_data['fob_location_qualifier'], + 'fob_location_description': order_data['fob_location_description'], + 'fob_title_passage_code': order_data['fob_title_passage_code'], + 'fob_title_passage_location': order_data['fob_title_passage_location'], + 'carrier_trans_method_code': order_data['carrier_trans_method_code'], + 'carrier_alpha_code': order_data['carrier_alpha_code'], + 'carrier_routing': order_data['carrier_routing'], + 'routing_sequence_code': order_data['routing_sequence_code'], + 'service_level_code': order_data['service_level_code'], + 'reference_qual': order_data['reference_qual'], + 'reference_id': order_data['reference_id'], + 'ref_description': order_data['ref_description'], + 'note_code': order_data['note_code'], + 'note_information_field': order_data['note_information_field'], + 'allow_chrg_indicator': order_data['allow_chrg_indicator'], + 'allow_chrg_code': order_data['allow_chrg_code'], + 'allow_chrg_agency_code': order_data['allow_chrg_agency_code'], + 'allow_chrg_agency': order_data['allow_chrg_agency'], + 'allow_chrg_amt': order_data['allow_chrg_amt'], + 'allow_chrg_percent_qual': order_data['allow_chrg_percent_qual'], + 'allow_chrg_percent': order_data['allow_chrg_percent'], + 'allow_chrg_handling_code': order_data['allow_chrg_handling_code'], + 'reference_identification': order_data['reference_identification'], + 'allow_chrg_handling_description': + order_data['allow_chrg_handling_description'], + } + + print "Attempting to create %s" % sale_hash + sale_id = sock.execute(DBNAME, uid, PWD, 'sale.order', 'create', sale_hash) + print "sale_idddddddddddddddddddddddd", sale_id + return sale_id + + +def search_uom(sock, uid, uom): + + uom_id = '' + + args = [('name', 'ilike', uom)] + ids = sock.execute(DBNAME, uid, PWD, 'product.uom', 'search', args) + try: + uom_id = ids[0] + except Exception: + pass + + return uom_id + + +def create_so_lines(sock, uid, sale_id, order_lines): + + sale_line_ids = [] + + for line in order_lines: + p_id = search_product(sock, uid, order_lines['sku']) + + order_line_hash = { + 'name': p_id.default_code, + 'edi_line_num': line['edi_line_num'], + 'product_uos_qty': int(line['product_qty']), + 'product_uom_qty': int(line['product_qty']), + 'edi_line_qty': int(line['product_qty']), + 'state': 'draft', + 'order_id': sale_id, # from sale order we just created + 'invoiced': False, + 'th_weight': 0, # lookup from product + 'product_id': p_id, + # passed in from PO + 'price_unit': line['purchase_price_basis'], + # lookup from product + 'purchase_price': line['purchase_price_basis'], + 'product_uom': line['product_uom'], # lookup from product + 'product_qty': int(line['product_qty']), + 'sku': line['sku'], + 'edi_yes': line['edi_yes'], + 'asn_shipment': line['asn_shipment'], + 'po_number': line['po_number'], + 'buyer_part_number': line['buyer_part_number'], + 'ack_yes': line['ack_yes'], + 'edi_est_del_date': line['edi_est_del_date'], + 'edi_est_ship_date': line['edi_est_ship_date'], + 'edi_line_msg': line['edi_line_msg'], + 'ship_not_before_date': line['ship_not_before_date'], + 'cancel_after_date': line['cancel_after_date'], + 'trading_partner_id': line['trading_partner_id'], + 'edi_intransit_qty': line['edi_intransit_qty'], + 'edi_outgoing_qty': line['edi_outgoing_qty'], + 'edi_avlforsale_qty': line['edi_avlforsale_qty'], + 'vendor_part_number': line['vendor_part_number'], + 'consumer_package_code': line['consumer_package_code'], + 'gtin': line['gtin'], + 'upc_case_code': line['upc_case_code'], + 'purchase_price_basis': line['purchase_price_basis'], + 'product_size_code': line['product_size_code'], + 'product_size_description': line['product_size_description'], + 'product_color_code': line['product_color_code'], + 'product_color_description': line['product_color_description'], + 'product_material_description': + line['product_material_description'], + 'department': line['department'], + 'classs': line['classs'], + 'price_type_id_code': line['price_type_id_code'], + 'item_description_type': line['item_description_type'], + 'product_characteristic_code': line['product_characteristic_code'], + 'agency_qualifier_code': line['agency_qualifier_code'], + 'product_description_code': line['product_description_code'], + 'pack_qualifier': line['pack_qualifier'], + 'pack_value': line['pack_value'], + 'pack_size': line['pack_size'], + 'pack_uom': line['pack_uom'], + 'packing_medium': line['packing_medium'], + 'packing_material': line['packing_material'], + 'pack_weight': line['pack_weight'], + 'pack_weight_uom': line['pack_weight_uom'], + 'location_code_qualifier': line['location_code_qualifier'], + 'location': line['location'], + 'allow_chrg_indicator': line['allow_chrg_indicator'], + 'allow_chrg_code': line['allow_chrg_code'], + 'allow_chrg_agency_code': line['allow_chrg_agency_code'], + 'allow_chrg_agency': line['allow_chrg_agency'], + 'allow_chrg_amt': line['allow_chrg_amt'], + 'allow_chrg_percent': line['allow_chrg_percent'], + 'percent_dollar_basis': line['percent_dollar_basis'], + 'allow_chrg_rate': line['allow_chrg_rate'], + 'allow_chrg_handling_code': line['allow_chrg_handling_code'], + 'allow_chrg_qty_uom': line['allow_chrg_qty_uom'], + } + try: + line_id = sock.execute( + DBNAME, uid, PWD, 'sale.order.line', 'create', order_line_hash) + sale_line_ids.append(line_id) + except BaseException: + pass + return sale_line_ids + + +def main(): + # connect to openerp + sock, uid = connect_oerp() + company_id = False + + if not company_id: + company_id = edi_850.get_current_company(sock, uid) + # read in incoming EDI, parse through and create dictionary with EDI info + files = os.listdir(IN_PATH) + # loop through files + for fle in files: + status, partner = parse_csv(sock, uid, IN_PATH + fle) + # get edi_config_id from company record + edi_id = edi_850.edi_config_id(sock, uid, company_id, partner) + # print 'EDI: ' + str(edi_id) + config = edi_850.get_config(sock, uid, edi_id) + # move processed file to archive folder + if not DEBUG and status: + if config['archive_path']: + shutil.move(IN_PATH + fle, config['archive_path'] + '/' + fle) + + +if __name__ == '__main__': + print 'Process: EDI Orders Read - Starting' + main() + print 'Process: EDI Orders Read - Ending' diff --git a/connector_spscommerce/edi_scripts/monitor.py b/connector_spscommerce/edi_scripts/monitor.py new file mode 100644 index 0000000..2acfdec --- /dev/null +++ b/connector_spscommerce/edi_scripts/monitor.py @@ -0,0 +1,149 @@ +#!/usr/bin/python +# Copyright (c) Open Source Integrators +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +import ftplib +import re +import time +import logging +import os +import shutil + +HOST = '' # IP Address +PORT = 22 +UNAME = '' +PW = '' + +SOURCE_DIR = '/out/' +DEST_DIR = '/in/' +REMOTE_ARCHIVE = '/Archive/' +LOCAL_DEST_DIR = '/var/log/_edi/Inbox/' +LOCAL_SOURCE_DIR = '/var/log/_edi/Outbox/' +ARCHIVE = '/var/log/_edi/Archive' + +log_path = '/var/log/_edi/Logs' + +count = 0 + +print "Current date & time " + time.strftime("%c") + + +def ftp_connect(HOST, UNAME, PW): + + ftp = ftplib.FTP(HOST) + try: + ftp.login(UNAME, PW) + logging.info('Successfully connected to the remote site.') + + except Exception: + logging.error('Error logging into ftp server.') + + return ftp + + +def process_files(ftp, SOURCE_DIR, LOCAL_SOURCE_DIR, DEST_DIR, LOCAL_DEST_DIR): + in_data = [] + out_data = [] + in_file_list = [] + out_file_list = [] + + ftp.dir(SOURCE_DIR, in_data.append) + + for in_file in in_data: + + file = re.split('\s+', in_file) + filename = file[len(file) - 1] + filename.replace(" ", "") + + if filename not in ['.', '..', 'Archive']: + in_file_list.append(filename) + + out_data = os.listdir(LOCAL_SOURCE_DIR) + + for out_file in out_data: + + file = re.split('\s+', out_file) + filename = file[len(file) - 1] + filename.replace(" ", "") + + if filename not in ['.', '..', 'Archive']: + out_file_list.append(filename) + + logging.info('Found this list of incoming files: %s' % str(in_file_list)) + logging.info('Found this list of outgoing files: %s' % str(out_file_list)) + + # Create lists of each file type for outgoing and incoming files + incoming_files = [fle for fle in in_file_list] + outgoing_files = [fle for fle in out_file_list] + + get = transfer_files(ftp, incoming_files, SOURCE_DIR, + LOCAL_DEST_DIR, 'RETR') + + if get: + for filename in in_file_list: + + try: + ftp.rename(SOURCE_DIR + filename, REMOTE_ARCHIVE + filename) + logging.info('Successfully transfered %s' % + filename + 'from remote host') + print 'Successful Transfer of: ' + filename + \ + ' from remote host' + except Exception: + logging.error( + 'Failed to archive %s on remote server' % filename) + + send = transfer_files(ftp, outgoing_files, + LOCAL_SOURCE_DIR, DEST_DIR, 'STOR') + + if send: + for filename in out_file_list: + + try: + shutil.move(LOCAL_SOURCE_DIR + filename, ARCHIVE) + logging.info('Successfully transfered %s' % + filename + 'to remote host') + print 'Successful Transfer of: ' + filename + ' to remote host' + except Exception: + logging.error( + 'Failed to archive %s on local server' % filename) + + ftp.quit() + + +def transfer_files(ftp, files, SOURCE_DIR, DEST_DIR, transfer_type): + + res = True + + for filename in files: + + try: + + if transfer_type == 'RETR': + outfile = open(DEST_DIR + filename, 'w') + ftp.retrlines('RETR' + ' ' + SOURCE_DIR + filename, + lambda s, w=outfile.write: w(s + '\n')) + outfile.close() + if transfer_type == 'STOR': + ftp.cwd(DEST_DIR) + ftp.storbinary("STOR " + filename, + open(SOURCE_DIR + filename, "rb")) + + except Exception: + + res = False + logging.error('Error transferring ' + filename + + ' using transfer type: ' + transfer_type) + + return res + + +def main(): + + ftp = ftp_connect(HOST, UNAME, PW) + process_files(ftp, SOURCE_DIR, LOCAL_SOURCE_DIR, DEST_DIR, LOCAL_DEST_DIR) + + +if __name__ == '__main__': + print 'Starting' + main() + print 'Sleeping' + time.sleep(5) diff --git a/connector_spscommerce/edi_scripts/test.py b/connector_spscommerce/edi_scripts/test.py new file mode 100644 index 0000000..1271782 --- /dev/null +++ b/connector_spscommerce/edi_scripts/test.py @@ -0,0 +1,145 @@ +#!/usr/bin/python +# Copyright (c) Open Source Integrators +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +import csv +import itertools +import xmlrpclib +import edi_850 +from connect_info import ERP_WWW, DBNAME, USERNAME, PWD, IN_PATH + + +def connect_oerp(): + ''' + Function that will make a connection to OpenERP using XML-RPC. + @return1: socket that is connected to OpenERP + @return2: An id that shows that you have been validated + ''' + sock = xmlrpclib.ServerProxy(ERP_WWW + '/xmlrpc/object') + sock_common = xmlrpclib.ServerProxy(ERP_WWW + '/xmlrpc/common') + uid = sock_common.login(DBNAME, USERNAME, PWD) + + print "XMLRPC Connection: SUCCESS - SERVER HAS AUTHENTICATED A LOGIN" + + return sock, uid + + # CSV COLUMNS + cols = [ + 'partner', + 'po_num', + 'date', 'ship_to_code', + 'sku', + 'upc', + 'product_desc', + 'product_qty', + 'product_uom', + 'price_unit', + 'third_party', + 'address1', + 'address2', + 'city', + 'state', + 'zip', + 'country', + 'billing_contact', + 'billing_address1', + 'billing_address2', + 'billing_city', + 'billing_state', + 'billing_zip', + 'billing_country', + 'ship_not_before', + 'cancel_after', + 'po_line' + ] + + csv_obj = open(file, 'rb') + data = csv.reader(csv_obj, delimiter='\t') + count = 1 + record = {} + record['sku'] = [] + record['upc'] = [] + record['po_line'] = [] + + for col in itertools.islice(cols, 6, 10): + record[col] = [] + + for row in data: + + if count == 1 and row[0] != 'H': + + print "INFO: FAILURE - YOUR FILE DOES NOT HAVE THE REQUIRED FIRST" \ + " ROW WITH COLUMN HEADINGS:" \ + " EDI Loop | Order line | Partner | PO # | Date/UPC |" \ + " code?/product desc | quantity | UOM Price *****" + break + + if count > 2 and row[0] == '': + # sale_id, sale_name = process_record(sock, uid, record) + break + + if row[0] == 'H': + + if record['sku']: + print record + edi_850.main(sock, uid, record, IN_PATH) + record['sku'] = [] + record['upc'] = [] + record['po_line'] = [] + + for col in itertools.islice(cols, 6, 10): + record[col] = [] + + record['ship_to_code'] = row[14] + record['ship_not_before'] = row[31] + record['cancel_after'] = row[32] + + # get partner, PO#, date + i = 2 + for col in itertools.islice(cols, 0, 3): + record[col] = row[i] + i += 1 + + # get thirdparty info + i = 7 + for col in itertools.islice(cols, 10, 17): + record[col] = row[i] + i += 1 + + # get billing address info + i = 15 + for col in itertools.islice(cols, 17, 24): + record[col] = row[i] + i += 1 + + elif row[0] == 'I': + + record['sku'].append(row[2]) + record['upc'].append(row[4]) + record['po_line'].append(row[1]) + # get sale line data: sku, asin, product description, quantity, + # uom, price + i = 5 + for col in itertools.islice(cols, 6, 10): + record[col].append(row[i]) + i += 1 + + count += 1 + + status = edi_850.main(sock, uid, record, IN_PATH) + + csv_obj.close() + + return status, record['partner'] + + +def main(): + sock, uid = connect_oerp() + o_id = 33382 + orders = sock.execute(DBNAME, uid, PWD, 'stock.picking', 'read', o_id) + print orders + + +if __name__ == '__main__': + print 'Process: EDI Orders Read - Starting' + main() + print 'Process: EDI Orders Read - Ending' diff --git a/connector_spscommerce/models/__init__.py b/connector_spscommerce/models/__init__.py new file mode 100644 index 0000000..6dc5d51 --- /dev/null +++ b/connector_spscommerce/models/__init__.py @@ -0,0 +1,12 @@ +# -*- coding: utf-8 -*- +# Copyright (c) Open Source Integrators +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from . import ( + edi_config, + sale, + purchase, + procurement, + stock, + stock_move, + account_invoice +) diff --git a/connector_spscommerce/models/account_invoice.py b/connector_spscommerce/models/account_invoice.py new file mode 100644 index 0000000..c9ccad3 --- /dev/null +++ b/connector_spscommerce/models/account_invoice.py @@ -0,0 +1,395 @@ +# -*- coding: utf-8 -*- +# Copyright (c) Open Source Integrators +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from datetime import datetime +import re +import dicttoxml +from openerp.osv import fields, osv +import openerp.addons.decimal_precision as dp + + +class account_invoice_line(osv.osv): + _inherit = "account.invoice.line" + + _columns = { + 'asn_shipment': fields.char('ASN Shipment Number from Converted 856'), + 'po_number': fields.char('Line Item PO Number from Converted 856'), + 'buyer_part_number': fields.char(string='Buyer Part Number'), + 'edi_line_qty': fields.float('Original EDI Quantity', digits_compute=dp.get_precision('Product Unit of Measure')), + 'vendor_part_number': fields.char('VendorPartNumber'), + 'consumer_package_code': fields.char('ConsumerPackageCode'), + 'gtin': fields.char('GTIN'), + 'upc_case_code': fields.char('UPCCaseCode'), + 'purchase_price_basis': fields.char('PurchasePriceBasis'), + 'product_size_code': fields.char('ProductSizeCode'), + 'product_size_description': fields.char('ProductSizeDescription'), + 'product_color_code': fields.char('ProductColorCode'), + 'product_color_description': fields.char('ProductColorDescription'), + 'product_material_code': fields.char('ProductMaterialCode'), + 'product_material_description': fields.char('ProductMaterialDescription'), + 'department': fields.char('Department'), + 'classs': fields.char('Class'), + 'price_type_id_code': fields.char('PriceTypeIDCode'), + 'edi_line_num': fields.integer('EDI PO line number'), + 'multiple_price_quantity': fields.float('MultiplePriceQuantity'), + 'class_of_trade_code': fields.char('ClassOfTradeCode'), + 'item_description_type': fields.char('ItemDescriptionType'), + 'product_characteristic_code': fields.char('ProductCharacteristicCode'), + 'agency_qualifier_code': fields.char('AgencyQualifierCode'), + 'product_description_code': fields.char('ProductDescriptionCode'), + 'pack_qualifier': fields.char('PackQualifier'), + 'pack_value': fields.integer('PackValue'), + 'pack_size': fields.char('PackSize'), + 'pack_uom': fields.char('PackUOM'), + 'packing_medium': fields.char('PackingMedium'), + 'packing_material': fields.char('PackingMaterial'), + 'pack_weight': fields.float('PackWeight'), + 'pack_weight_uom': fields.char('PackWeightUOM'), + 'location_code_qualifier':fields.char('LocationCodeQualifier', help="Code identifying the structure or format of the related location number(s)."), + 'location':fields.char('Location', help="For CrossDock, it's the marked for location. For MultiStore[could also be DC] ship-to location."), + 'allow_chrg_indicator':fields.char('AllowChrgIndicator', help="Code which indicates an allowance or charge for the service specified."), + 'allow_chrg_code':fields.char('AllowChrgCode', help="Code describing the type of allowance or charge for the service specified."), + 'allow_chrg_agency_code':fields.char('AllowChrgAgencyCode', help="Code identifying the agency assigning the code values."), + 'allow_chrg_agency':fields.char('AllowChrgAgency', help="Agency maintained code identifying the service, promotion, allowance, or charge."), + 'allow_chrg_amt':fields.float('AllowChrgAmt', help="Amount of the allowance or charge."), + 'allow_chrg_percent':fields.float('AllowChrgPercent', help="Percentage of allowance or charge. Percentages should be represented as real numbers[0% through 100% should be normalized to 0.0 through 100.00].."), + 'percent_dollar_basis':fields.float('PercentDollarBasis', help="."), + 'allow_chrg_rate':fields.float('AllowChrgRate', help="Amount of the allowance or charge."), + 'allow_chrg_handling_code':fields.char('AllowChrgHandlingCode', help="Code indicating method of handling for an allowance or charge.."), + 'allow_chrg_qty_uom':fields.char('AllowChrgQtyUOM', help=""), + 'allow_chrg_handling_description':fields.char('AllowChrgHandlingDescription', help="Free-form textual description of the note."), + } + + +class account_invoice(osv.osv): + _inherit = "account.invoice" + + _columns = { + 'asn_shipment': fields.char('ASN Shipment Number from Converted 856'), + 'client_order_ref': fields.text('Customer PO #', help="Customer PO #"), + 'ship_to_code': fields.char('Ship To Warehouse', help="Trading Partner Ship to location code."), + 'supplier_code': fields.char('Supplier Code', help="This is the date from the 856."), + 'edi_yes': fields.boolean('From an EDI PO?', readonly=True, help="Is this order from an EDI purchase order, 850 EDI doc."), + '810_sent_timestamp': fields.datetime('810 Sent Date', help="The timestamp for when the 856 was sent."), + 'trading_partner_id': fields.many2one('edi.config', 'Trading Partner', help='EDI Configuration information for partner'), + 'invoice_check': fields.boolean('810 EDI Invoice Sent?', readonly=True, help="Is checked if EDI 810 invoice is sent."), + 'ship_not_before_date': fields.date('Estimated Shipping Date', help="This is the date from the 856."), + 'cancel_after_date': fields.date('Cancel if Shipped After This Date', help="Cancel if Shipped After This Date."), + 'sale_id': fields.many2one('sale.order', 'Sale Order', help='Sale Order from Whence this Invoice Was created'), + 'picking_id': fields.many2one('stock.picking','Picking ID', help='Stock Picking ID whence this invoice was created'), + 'scac_code': fields.char('SCAC Code', help="This is the shipping alpha code from your carrier."), + 'bol_num': fields.char('BoL Number', help="This is bill of lading number from your carrier/shipper."), + 'tracking_num': fields.char('Supplier Code', help="This is the tracking number from your carrier."), + 'sender_id':fields.char(string='EDI Sender Code',), + 'tset_purpose_code':fields.char('TsetPurposeCode', help="Code identifying purpose of the document.."), + 'purchase_order_type_code':fields.char('PurchaseOrderTypeCode', help="Code specifying the type of purchase order."), + 'po_type_description':fields.char('POTypeDescription', help="Free form text to describe the type of order."), + 'ship_complete_code':fields.char('ShipCompleteCode', help="Code to identify a specific requirement or agreement of sale. Should only be used to indicate if an item can be placed on backorder."), + 'department':fields.char('Department', help="Name or number identifying an area wherein merchandise is categorized within a store."), + 'division':fields.char('Division', help="Different entities belonging to the same parent company."), + 'promotion_deal_number':fields.char('PromotionDealNumber', help="Number uniquely identifying an agreement for a special offer or price."), + 'carrier_pro_number':fields.char('CarrierProNumber', help=""), + 'bill_of_lading_number':fields.char('BillOfLadingNumber', help=""), + 'terms_type':fields.char('TermsType', help="Code identifying type of payment terms."), + 'terms_basis_date_code':fields.char('TermsBasisDateCode', help="Code identifying the beginning of the terms period."), + 'terms_discount_percentage':fields.char('TermsDiscountPercentage', help="Terms discount percentage available to the purchaser"), + 'terms_discount_due_days':fields.char('TermsDiscountDueDays', help="Number of days by which payment or invoice must be received in order to receive the discount noted."), + 'terms_net_due_days':fields.char('TermsNetDueDays', help="Number of days until total invoice amount is due[discount not applicable."), + 'payment_method_code':fields.char('PaymentMethodCode', help="Indication of the instrument of payment."), + 'fob_pay_code':fields.char('FOBPayCode', help="Code identifying payment terms for transportation charges."), + 'fob_location_qualifier':fields.char('FOBLocationQualifier', help="Code identifying type of location at which ownership of goods is transferred."), + 'fob_location_description':fields.char('FOBLocationDescription', help="Free-form textual description of the location at which ownership of goods is transferred."), + 'fob_title_passage_code':fields.char('FOBTitlePassageCode', help="Code describing the location of ownership of the goods."), + 'fob_title_passage_location':fields.char('FOBTitlePassageLocation', help="Location of ownership of the goods."), + 'carrier_trans_method_code':fields.char('CarrierTransMethodCode', help="Code specifying the method or type of transportation for the shipment."), + 'carrier_alpha_code':fields.char('CarrierAlphaCode', help="Standard Carrier Alpha Code[SCAC] - "), + 'carrier_routing':fields.char('CarrierRouting', help="Free-form description of the routing/requested routing for shipment or the originating carrier's identity."), + 'routing_sequence_code':fields.char('RoutingSequenceCode', help=""), + 'service_level_code':fields.char('ServiceLevelCode', help="Code indicating the level of transportation service or the billing service offered by the transportation carrier."), + 'reference_qual':fields.char('ReferenceQual', help="Code specifying the type of data in the ReferenceID/ReferenceDescription."), + 'reference_id':fields.char('ReferenceID', help="Code specifying the type of data in the ReferenceID/ReferenceDescription."), + 'ref_description':fields.char('Description', help="Free-form textual description to clarify the related data elements and their content."), + 'note_code':fields.char('NoteCode', help="Code specifying the type of note."), + 'note_information_field':fields.char('NoteInformationField', help="Free-form textual description of the note."), + 'allow_chrg_indicator':fields.char('AllowChrgIndicator', help="Code which indicates an allowance or charge for the service specified."), + 'allow_chrg_code':fields.char('AllowChrgCode', help="Code describing the type of allowance or charge for the service specified."), + 'allow_chrg_agency_code':fields.char('AllowChrgAgencyCode', help="Code identifying the agency assigning the code values."), + 'allow_chrg_agency':fields.char('AllowChrgAgency', help="Agency maintained code identifying the service, promotion, allowance, or charge."), + 'allow_chrg_amt':fields.float('AllowChrgAmt', help="Amount of the allowance or charge."), + 'allow_chrg_percent_qual':fields.char('AllowChrgPercentQual', help="Code indicating on what basis an allowance or charge percent is calculated.."), + 'allow_chrg_percent':fields.float('AllowChrgPercent', help="Percentage of allowance or charge. Percentages should be represented as real numbers[0% through 100% should be normalized to 0.0 through 100.00].."), + 'allow_chrg_handling_code':fields.char('AllowChrgHandlingCode', help="Code indicating method of handling for an allowance or charge.."), + 'reference_identification':fields.char('ReferenceIdentification', help=""), + 'allow_chrg_handling_description':fields.char('AllowChrgHandlingDescription', help="Free-form textual description of the note."), + } + + def create_text_810(self, cr, uid, invoice_ids, context=None): + + today = str(datetime.now().strftime('%Y%m%d')) or '' + processed = 0 + num = '' + # for each invoice + invoices_list = {'Invoices':[]} + for invoice in self.browse(cr, uid, invoice_ids, context=context): + num += invoice.number + #skip non customer invoice records + if invoice.type not in ('out_invoice') or invoice.state in ('draft','cancel','paid'): + continue + + #Grab the vendor_id and trading_partner_id for EDI in the edi_config. + trading_partner_code = invoice.trading_partner_id.partner_header_string + vendor_code = invoice.trading_partner_id.vendor_header_string + + date_format = "%Y-%m-%d" + #invoice date + inv_date = invoice.date_invoice + dateinv_object = datetime.strptime(inv_date, date_format) + inv_date = dateinv_object.date() + inv_date = str(inv_date) + + #purchase date + po_date = invoice.purchase_id and invoice.purchase_id.date_order or '' + if po_date: + datepo_object = datetime.strptime(po_date, date_format) + po_date = datepo_object.date() + po_date = str(po_date) + + #ship date + ship_date = invoice.picking_id and invoice.picking_id.date_done or '' + if ship_date: + ship_object = datetime.strptime(ship_date, date_format) + ship_date = ship_object.date() + ship_date = str(ship_date) + + # initialize + invoice_dict = {'Invoice':{}} + # 1. Header Line - one line for the order + meta_dict = {'Meta': {'Version': '1.0'}} + header_dict = {'Header': {'InvoiceHeader': { + 'TradingPartnerId': trading_partner_code, + 'InvoiceNumber': str(invoice.number), + 'InvoiceDate':inv_date, + 'PurchaseOrderDate':po_date, + 'PurchaseOrderNumber':invoice.purchase_id and invoice.purchase_id.number or '', + 'ReleaseNumber':str(invoice.number), + 'InvoiceTypeCode':'U5', + 'BuyersCurrency':invoice.currency_id and invoice.currency_id.name or '', + 'Department':invoice.department or '', + 'Vendor':vendor_code, + 'PromotionDealNumber':invoice.promotion_deal_number or '', + 'CarrierProNumber':invoice.carrier_pro_number or '', + 'BillOfLadingNumber':invoice.bill_of_lading_number, + 'ShipDate':ship_date, + 'CustomerOrderNumber':invoice.sale_id and invoice.sale_id.name or '', + }, + 'PaymentTerms': { + 'TermsType':invoice.terms_type or '', + 'TermsBasisDateCode':invoice.terms_basis_date_code or '', + 'TermsDiscountPercentage':invoice.terms_discount_percentage or '', + 'TermsDiscountDate':inv_date, + 'TermsDiscountDueDays':invoice.terms_discount_due_days or '', + 'TermsNetDueDate':inv_date, + 'TermsNetDueDays':invoice.terms_net_due_days or '', + 'TermsDiscountAmount':0, + 'TermsDescription':invoice.payment_term_id.note or '', + }, + 'Date': { + 'DateTimeQualifier1':'196', + 'Date1':inv_date, + 'Time1':'', + }, + 'Contact':{ + 'ContactTypeCode':'BD', + 'ContactName':invoice.picking_id and invoice.picking_id.partner_id and invoice.picking_id.partner_id.name or '', + 'PrimaryPhone':invoice.picking_id and invoice.picking_id.partner_id and invoice.picking_id.partner_id.phone or '', + 'PrimaryFax':invoice.picking_id and invoice.picking_id.partner_id and invoice.picking_id.partner_id.fax or '', + 'PrimaryEmail':invoice.picking_id and invoice.picking_id.partner_id and invoice.picking_id.partner_id.email or '', + }, + 'Address':{ + 'AddressTypeCode':'Z7', + 'LocationCodeQualifier':'92', + 'AddressLocationNumber':'11111', + 'AddressName':invoice.partner_id.name, + 'Address1':invoice.partner_id.street, + 'Address2':invoice.partner_id.street2, + 'City':invoice.partner_id.city, + 'State':invoice.partner_id.state_id.name, + 'PostalCode':invoice.partner_id.zip, + 'Country':invoice.partner_id.country_id.name, + 'Contact':{ + 'ContactTypeCode':'BD', + 'ContactName':invoice.picking_id and invoice.picking_id.partner_id and invoice.partner_id.name or '', + 'PrimaryPhone':invoice.picking_id and invoice.picking_id.partner_id and invoice.partner_id.phone or '', + 'PrimaryFax':invoice.picking_id and invoice.picking_id.partner_id and invoice.partner_id.fax or '', + 'PrimaryEmail':invoice.picking_id and invoice.picking_id.partner_id and invoice.email or '', + }, + }, + 'Reference':{ + 'ReferenceQual':invoice.reference_qual or '', + 'ReferenceID':invoice.reference_id or '', + 'Description':invoice.ref_description or '', + }, + 'Notes':{ + 'NoteCode':invoice.note_code or '', + 'NoteInformationField':invoice.note_information_field or '', + }, + 'Tax':{ + 'TaxTypeCode':'S', + 'TaxAmount':'1845.08', + 'TaxPercent':'8.50', + 'JurisdictionQual':'CC', + 'JurisdictionCode':'07034', + 'TaxExemptCode':'2', + 'TaxID':'99990000', + }, + 'ChargesAllowances':{ + 'AllowChrgIndicator':invoice.allow_chrg_indicator or '', + 'AllowChrgCode':invoice.allow_chrg_code or '', + 'AllowChrgAmt':invoice.allow_chrg_amt or 0, + 'AllowChrgPercentQual':invoice.allow_chrg_percent_qual or '', + 'AllowChrgPercent':invoice.allow_chrg_percent or 0, + 'AllowChrgHandlingCode':invoice.allow_chrg_handling_code or '', + 'AllowChrgHandlingDescription':invoice.allow_chrg_handling_description or '', + }, + 'FOBRelatedInstruction':{ + 'FOBPayCode':invoice.fob_pay_code or '', + 'FOBLocationQualifier': invoice.fob_location_qualifier or '', + 'FOBLocationDescription':invoice.fob_location_description or '', + 'FOBTitlePassageCode': invoice.fob_title_passage_code or '', + 'FOBTitlePassageLocation':invoice.fob_title_passage_location or '', + }, + 'CarrierInformation':{ + 'CarrierTransMethodCode':invoice.carrier_trans_method_code or '', + 'CarrierAlphaCode':invoice.carrier_alpha_code or '', + 'CarrierRouting':invoice.carrier_routing or '', + 'CarrierEquipmentNumber':invoice.routing_sequence_code or '', + }, + 'ServiceLevelCodes':{ + 'ServiceLevelCode':invoice.service_level_code or '', + }, + } + } + invoice_dict.get('Invoice').update(meta_dict) + invoice_dict.get('Invoice').update(header_dict) + #LineItems + lineitems_list = {'LineItems': []} + total_lines = 0 + total_qty = 0 + total_weight = 0 + #for each item line + for line in invoice.invoice_line_ids: + total_lines += 1 + total_qty += line.quantity + total_weight += (line.product_id.weight * line.quantity) + lineitem_dict = {'LineItem':{}} + invoiceline_dict = {'InvoiceLine': { + 'LineSequenceNumber':int(line.id), + 'BuyerPartNumber':line.buyer_part_number or '', + 'VendorPartNumber':line.product_id.default_code or '', + 'ConsumerPackageCode':'093597609541', + 'EAN':line.product_id.barcode, + 'GTIN':line.gtin or '', + 'UPCCaseCode':line.upc_case_code or '', + 'NatlDrugCode':'51456-299', + 'InternationalStandardBookNumber':'999-0-555-22222-0', + 'ProductID':{ + 'PartNumberQual':'IS', + 'PartNumber':line.product_id.default_code, + }, + 'PurchasePrice':line.price_unit, + 'ShipQty':line.quantity, + 'ShipQtyUOM':'P4', + 'ProductSizeCode':line.product_size_code or '', + 'ProductSizeDescription':line.product_size_description or '', + 'ProductColorCode':line.product_color_code or '', + 'ProductColorDescription':line.product_color_description or '', + 'ProductMaterialDescription':line.product_material_description or '', + 'NRFStandardColorAndSize':{ + 'NRFColorCode':'600', + 'NRFSizeCode':'42-10651', + } + }, + 'ProductOrItemDescription':{ + 'ItemDescriptionType':line.item_description_type or '', + 'AgencyQualifierCode':line.agency_qualifier_code or '', + 'ProductDescriptionCode':line.product_id.default_code, + 'ProductDescription':line.name, + }, + 'PhysicalDetails':{ + 'PackQualifier': line.pack_qualifier or '', + 'PackValue':line.pack_value or 0, + 'PackSize':line.pack_size or '', + 'PackUOM':line.pack_uom or '', + }, + 'Tax':{ + 'TaxTypeCode':'S', + 'TaxAmount':'1845.08', + 'TaxPercent':'8.50', + 'JurisdictionQual':'CC', + 'JurisdictionCode':'07034', + 'TaxExemptCode':'2', + 'TaxID':'99990000', + }, + 'ChargesAllowances':{ + 'AllowChrgIndicator':line.allow_chrg_indicator or '', + 'AllowChrgCode':line.allow_chrg_code or '', + 'AllowChrgAmt':line.allow_chrg_amt or 0, + 'AllowChrgPercentQual':'', + 'AllowChrgPercent':line.allow_chrg_percent or 0, + 'AllowChrgHandlingCode':line.allow_chrg_handling_code or '', + 'AllowChrgHandlingDescription':line.allow_chrg_handling_description or '', + }, + } + lineitem_dict.get('LineItem').update(invoiceline_dict) + lineitems_list.get('LineItems').append(lineitem_dict) + + invoice_dict.get('Invoice').update(lineitems_list) + + # Summary + summary_dict = {'Summary': { + 'TotalAmount':invoice.amount_total or '', + 'TotalNetSalesAmount':invoice.amount_untaxed, + 'TotalTermsDiscountAmount':0, + 'TotalQtyInvoiced':total_qty, + 'TotalWeight':total_weight, + 'TotalLineItemNumber':total_lines, + 'InvoiceAmtDueByTermsDate':0, + 'TotalQtyInvoicedUOM':'P3', + 'TotalWeightUOM':'HD', + } + } + invoice_dict.get('Invoice').update(summary_dict) + # update invoice + today = datetime.now().strftime("%Y-%m-%d %H:%M:%S") + invoice.write({'810_sent_timestamp':today}) + invoices_list.get('Invoices').append(invoice_dict) + processed += 1 + # Convert dictionary to xml + xml = dicttoxml.dicttoxml(invoices_list, attr_type=False, root=False) + xml = xml.replace('','').replace('','') + xml = xml.replace('','').replace('','') + # Write ASN doc to text file + num = re.findall('\d+', num)[0] + filename = '810_' + today + '%s.xml' % num + filename.replace('/', '_') + fd = open(invoice.trading_partner_id.out_path + filename, 'w') + fd.write(xml) + fd.close() + return processed + + def _create_810_wrapper(self, cr, uid, context=None): + #search for invoices that are edi_yes = True and 810_sent_timestamp = False. + eligible_invoices = self.search(cr, uid, [('edi_yes','=',True),('810_sent_timestamp','=',False)], context=context) + return eligible_invoices and self.create_text_810(cr, uid, eligible_invoices, context=context) or False + + # done as a server action + def action_create_text_810(self, cr, uid, ids, context=None): + """ Creates a new 810 and puts it into the outbox + """ + if context is None: + context = {} + # number of orders to process + toprocess = len(ids) + # process orders to write 810 + processed = self.create_text_810(cr, uid, ids, context=context) + return (toprocess - processed) diff --git a/connector_spscommerce/models/edi_config.py b/connector_spscommerce/models/edi_config.py new file mode 100644 index 0000000..2915e16 --- /dev/null +++ b/connector_spscommerce/models/edi_config.py @@ -0,0 +1,81 @@ +# -*- coding: utf-8 -*- +# Copyright (c) Open Source Integrators +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from openerp.osv import fields, orm, osv + + +class edi_config(osv.osv): + _name = "edi.config" + _description = "EDI Configuration Systems" + + _columns = { + 'edi_company_id': fields.many2one('res.company', + string ='EDI Company', + help="The Main EDI Company.", + required=False, ondelete='cascade'), + 'route_id': fields.many2one('stock.location.route', + string ='Stock Transfer Route', + help="""Select Dropshipping if applicable + or another route. This will be the route + used in sale order lines."""), + 'vendor_header_string': fields.char(string='EDI Vendor ID',), + 'partner_header_string': fields.char(string='Partner Header ID',), + 'billto_header_string': fields.char(string='Bill to Header ID',), + 'salesperson': fields.many2one('res.partner', string='Sales Person', + help="The Salesperson for EDI Trading" + " Partner.", + required=False, ondelete='cascade'), + 'in_path': fields.char(string='EDI In Path',), + 'out_path': fields.char(string='EDI Out Path',), + 'log_path': fields.char(string='EDI Logs Path',), + 'archive_path': fields.char(string='EDI Archive Path',), + 'trading_partner_id': fields.many2one('res.partner', + string='Trading Partner', + help="The trading partner for" + " EDI.", + required=False, + ondelete='cascade'), + 'is_thirdparty': fields.boolean('Ship Directly to Customer'), + 'is_sku': fields.boolean('Use SKU instead of UPC'), + 'auto_workflow': fields.many2one('sale.workflow.process', + string='Automatic Workflow', + ondelete='restrict'), + 'ack_855': fields.boolean('EDI 855 Ack'), + 'ack_997': fields.boolean('EDI 997 Ack'), + } + + def name_get(self, cr, uid, ids, context=None): + if context is None: + context = {} + res = [] + for record in self.browse(cr, uid, ids, context=context): + res.append((record['id'], record.trading_partner_id.name)) + return res + + +class company_config(orm.Model): + _inherit = 'res.company' + + _columns = { + 'header_string': fields.char(string='Company Header String',), + 'trading_partner_id': fields.many2many('edi.config', + string='EDI Trading Partner', + required=False, + ondelete='cascade'), + } + + +class res_partner(orm.Model): + _inherit = 'res.partner' + + _columns = { + 'trading_partner_res': fields.one2many('edi.config', + 'trading_partner_id', + required=False, + ondelete='cascade', + help=''), + 'edi_ids': fields.one2many('edi.config', 'edi_company_id', + 'EDI Company ID'), + 'ship_to_code': fields.char(string='Ship to Code',), + 'sender_id': fields.char(string='EDI Sender Code',), + } \ No newline at end of file diff --git a/connector_spscommerce/models/procurement.py b/connector_spscommerce/models/procurement.py new file mode 100644 index 0000000..101f199 --- /dev/null +++ b/connector_spscommerce/models/procurement.py @@ -0,0 +1,58 @@ +# -*- coding: utf-8 -*- +# Copyright (c) Open Source Integrators +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from openerp.osv import fields, osv + + +class procurement_order(osv.osv): + _inherit = 'procurement.order' + + _columns = { + 'po_number': fields.char('Line Item PO Number from Converted 856'), + 'edi_line_num': fields.integer('EDI PO Line #'), + 'asn_shipment': fields.char('ASN Shipment Number'), + 'ship_to_code': fields.char('Ship To Warehouse', + help="Trading Partner Ship to location" + " code."), + 'sale_line_id': fields.many2one('sale.order.line', 'Sale Order Line', + help='Sale Order Line from whence this' + ' Stock Move Was created'), + 'so_id': fields.many2one('sale.order', 'Sale Order', + help='Sale Order from Whence this Invoice was' + ' created'), + 'edi_yes': fields.boolean('From an EDI PO?', readonly=True, + help="Is this order from an EDI purchase" + " order, 850 EDI doc."), + 'est_del_date': fields.date('Estimated Delivery Date', + help="Calculated based on shipping method."), + 'trading_partner_id': fields.many2one('edi.config', 'Trading Partner', + help='EDI Configuration' + ' information for partner'), + 'ship_not_before_date': fields.date('Do Not Ship Before This Date', + help="Do Not Ship Before This" + " Date."), + 'cancel_after_date': fields.date('Cancel if Shipped After This Date', + help="Cancel if Shipped After This" + " Date."), + } + + def _run_move_create(self, cr, uid, procurement, context=None): + + res = super(procurement_order, self).\ + _run_move_create(cr, uid, procurement=procurement, context=context) + res['po_number'] = procurement.po_number or '' + res['ship_to_code'] = procurement.ship_to_code or '' + res['asn_shipment'] = procurement.asn_shipment or '' + res['sale_line_id'] = procurement.sale_line_id.id or False + res['edi_line_num'] = procurement.edi_line_num or 0 + res['so_id'] = procurement.so_id.id or False + res['trading_partner_id'] = procurement.trading_partner_id and\ + procurement.trading_partner_id.id or False + res['edi_yes'] = procurement.trading_partner_id and\ + procurement.edi_yes or False + res['ship_not_before_date'] = procurement.trading_partner_id and\ + procurement.ship_not_before_date or False + res['cancel_after_date'] = procurement.trading_partner_id and\ + procurement.cancel_after_date or False + + return res diff --git a/connector_spscommerce/models/purchase.py b/connector_spscommerce/models/purchase.py new file mode 100644 index 0000000..80c29ef --- /dev/null +++ b/connector_spscommerce/models/purchase.py @@ -0,0 +1,12 @@ +# -*- coding: utf-8 -*- +# Copyright (c) Open Source Integrators +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from openerp.osv import fields, osv + +class purchase_order_line(osv.osv): + _inherit = "purchase.order.line" + + _columns = { + 'asn_shipment': fields.char('ASN Shipment Number from Converted 856'), + 'po_number': fields.char('Line Item PO Number from Converted 856'), + } diff --git a/connector_spscommerce/models/sale.py b/connector_spscommerce/models/sale.py new file mode 100644 index 0000000..aa32045 --- /dev/null +++ b/connector_spscommerce/models/sale.py @@ -0,0 +1,633 @@ +# -*- coding: utf-8 -*- +# Copyright (c) Open Source Integrators +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +import re +from datetime import datetime, timedelta +from random import randint +from openerp import api, models +import openerp.addons.decimal_precision as dp +from openerp.osv import fields, osv +from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT + + +class SaleOrder(models.Model): + _inherit = "sale.order" + + _columns = { + 'asn_shipment': fields.char('ASN Number from 856'), + 'edi_error': fields.text('EDI Errors', help="Text that will describe failures to add sale order lines because of failing product lookups."), + 'edi_yes': fields.boolean('From an EDI PO?', help="Is this order from an EDI purchase order, 850 EDI doc."), + 'ack_yes': fields.boolean('855', help="Will this order have an 855?"), + '855_replace': fields.boolean('Send 855', readonly=True, help="Shall we send an 855 replacement?"), + 'ship_to_code': fields.char('Ship To Warehouse', help="Trading Partner Ship to location code."), + 'supplier_code': fields.char('Supplier Code', help="Supplier code from the 856."), + 'ship_not_before_date': fields.date('Estimated Shipping Date', help="This is the date from the 856."), + 'cancel_after_date': fields.date('Cancel if Shipped After This Date', help="Cancel if Shipped After This Date."), + 'trading_partner_id': fields.many2one('edi.config', 'Trading Partner', help='EDI Configuration information for partner'), + '855_sent_timestamp': fields.datetime('855 Sent Date', help="The timestamp for when the 855 was sent."), + '855_check': fields.boolean('855 Sent Already', help="A check to see if 855 has been sent already."), + 'scac_code': fields.char('SCAC Code', help="This is the shipping alpha code from your carrier."), + 'bol_num': fields.char('BoL Number', help="This is bill of lading number from your carrier/shipper."), + 'tracking_num': fields.char('Tracking Number', help="This is the tracking number from your carrier."), + 'tset_purpose_code':fields.char('TsetPurposeCode', help="Code identifying purpose of the document.."), + 'purchase_order_type_code':fields.char('PurchaseOrderTypeCode', help="Code specifying the type of purchase order."), + 'po_type_description':fields.char('POTypeDescription', help="Free form text to describe the type of order."), + 'ship_complete_code':fields.char('ShipCompleteCode', help="Code to identify a specific requirement or agreement of sale. Should only be used to indicate if an item can be placed on backorder."), + 'department':fields.char('Department', help="Name or number identifying an area wherein merchandise is categorized within a store."), + 'division':fields.char('Division', help="Different entities belonging to the same parent company."), + 'promotion_deal_number':fields.char('PromotionDealNumber', help="Number uniquely identifying an agreement for a special offer or price."), + 'terms_type':fields.char('TermsType', help="Code identifying type of payment terms."), + 'terms_basis_date_code':fields.char('TermsBasisDateCode', help="Code identifying the beginning of the terms period."), + 'terms_discount_percentage':fields.char('TermsDiscountPercentage', help="Terms discount percentage available to the purchaser"), + 'terms_discount_due_days':fields.char('TermsDiscountDueDays', help="Number of days by which payment or invoice must be received in order to receive the discount noted."), + 'terms_net_due_days':fields.char('TermsNetDueDays', help="Number of days until total invoice amount is due[discount not applicable."), + 'payment_method_code':fields.char('PaymentMethodCode', help="Indication of the instrument of payment."), + 'fob_pay_code':fields.char('FOBPayCode', help="Code identifying payment terms for transportation charges."), + 'fob_location_qualifier':fields.char('FOBLocationQualifier', help="Code identifying type of location at which ownership of goods is transferred."), + 'fob_location_description':fields.char('FOBLocationDescription', help="Free-form textual description of the location at which ownership of goods is transferred."), + 'fob_title_passage_code':fields.char('FOBTitlePassageCode', help="Code describing the location of ownership of the goods."), + 'fob_title_passage_location':fields.char('FOBTitlePassageLocation', help="Location of ownership of the goods."), + 'carrier_trans_method_code':fields.char('CarrierTransMethodCode', help="Code specifying the method or type of transportation for the shipment."), + 'carrier_alpha_code':fields.char('CarrierAlphaCode', help="Standard Carrier Alpha Code[SCAC] - "), + 'carrier_routing':fields.char('CarrierRouting', help="Free-form description of the routing/requested routing for shipment or the originating carrier's identity."), + 'routing_sequence_code':fields.char('RoutingSequenceCode', help=""), + 'service_level_code':fields.char('ServiceLevelCode', help="Code indicating the level of transportation service or the billing service offered by the transportation carrier."), + 'reference_qual':fields.char('ReferenceQual', help="Code specifying the type of data in the ReferenceID/ReferenceDescription."), + 'reference_id':fields.char('ReferenceID', help="Code specifying the type of data in the ReferenceID/ReferenceDescription."), + 'ref_description':fields.char('Description', help="Free-form textual description to clarify the related data elements and their content."), + 'note_code':fields.char('NoteCode', help="Code specifying the type of note."), + 'note_information_field':fields.char('NoteInformationField', help="Free-form textual description of the note."), + 'allow_chrg_indicator':fields.char('AllowChrgIndicator', help="Code which indicates an allowance or charge for the service specified."), + 'allow_chrg_code':fields.char('AllowChrgCode', help="Code describing the type of allowance or charge for the service specified."), + 'allow_chrg_agency_code':fields.char('AllowChrgAgencyCode', help="Code identifying the agency assigning the code values."), + 'allow_chrg_agency':fields.char('AllowChrgAgency', help="Agency maintained code identifying the service, promotion, allowance, or charge."), + 'allow_chrg_amt':fields.float('AllowChrgAmt', help="Amount of the allowance or charge."), + 'allow_chrg_percent_qual':fields.char('AllowChrgPercentQual', help="Code indicating on what basis an allowance or charge percent is calculated.."), + 'allow_chrg_percent':fields.float('AllowChrgPercent', help="Percentage of allowance or charge. Percentages should be represented as real numbers[0% through 100% should be normalized to 0.0 through 100.00].."), + 'allow_chrg_handling_code':fields.char('AllowChrgHandlingCode', help="Code indicating method of handling for an allowance or charge.."), + 'reference_identification':fields.char('ReferenceIdentification', help=""), + 'allow_chrg_handling_description':fields.char('AllowChrgHandlingDescription', help="Free-form textual description of the note."), + } + + def create_855(self, cr, uid, sale_ids, context=None): + + xml_output = 'testVersion
' + + for sale_id in sale_ids: + + # initialize + csvwriter=None + sale_obj = self.browse(cr,uid, sale_id, context=context) + config_obj = sale_obj.trading_partner_id + OUT_PATH = config_obj.out_path + sale_lines = sale_obj.order_line + sale_name = sale_obj.name + num = re.findall('\d+', sale_name)[0] + po_num = '' + + if sale_obj.client_order_ref: + po_num = sale_obj.client_order_ref + + #create and format dates for ACK + now = datetime.now() + today = now.strftime('%Y%m%d') + time = datetime.now().strftime('%H%M%S') + po_date = datetime.strptime(sale_obj.date_order,'%Y-%m-%d %H:%M:%S') + po_date = po_date.strftime('%m/%d/%Y') + + #generate random numbers for the 3 header id numbers in 855 + ISA_num = randint(100000000,999999999) + GS_code = randint(1000000,9999999) + st_trans_num = randint(100000000,999999999) + + #Grab the vendor_id and trading_partner_id for EDI in the edi_config. + trading_partner_code = sale_obj.trading_partner_id.partner_header_string + trading_partner_padded = trading_partner_code.ljust(15) + + vendor_code = sale_obj.trading_partner_id.vendor_header_string + vendor_padded = vendor_code.ljust(15) + OUT_PATH = str(sale_obj.trading_partner_id.out_path) + + #header, payment and date info + xml_output += '' +trading_partner_code+ '' + xml_output += '' +po_num+ '' + xml_output += ''+str(sale_obj.tset_purpose_code)+'' + xml_output += '' +po_date+ '' + xml_output += '' +str(sale_obj.purchase_order_type_code)+ '' + xml_output += '' +str(sale_obj.po_type_description)+ '' + xml_output += '' +po_num+ '' + xml_output += 'RJ' + xml_output += '' +today+ '' + xml_output += '' +str(sale_obj.ship_complete_code)+ '' + xml_output += ''+str(sale_obj.pricelist_id.currency_id.name)+'' + xml_output += '' +str(sale_obj.department)+ '' + xml_output += '' +str(sale_obj.division)+ '' + xml_output += '' +str(sale_obj.name)+ '' + xml_output += '' +str(sale_obj.promotion_deal_number)+ '' + xml_output += '' +vendor_code+ '' + xml_output += ''+str(sale_obj.terms_type)+'' + xml_output += '' +str(sale_obj.terms_basis_date_code)+ '' + xml_output += '' +str(sale_obj.terms_discount_percentage)+ '' + xml_output += '' +str(sale_obj.terms_discount_due_days)+ '' + xml_output += '' +str(sale_obj.terms_net_due_days)+ '' + xml_output += '' +str(sale_obj.payment_term_id.note)+ '' + xml_output += '' +str(sale_obj.payment_method_code)+ '' + xml_output += 'ORS' + xml_output += '' +today+ '' + cust_rec = sale_obj.partner_id + ship_address_rec = sale_obj.partner_shipping_id + + #customer contact details + xml_output += 'CH' + xml_output += '' +str(cust_rec.name)+ '' + xml_output += '' +str(cust_rec.phone)+ '' + xml_output += '' +str(cust_rec.fax)+ '' + xml_output += '' +str(cust_rec.email)+ '' + + #contact address details + xml_output += '
FW' + xml_output += '1' + xml_output += '11111' + xml_output += '' +str(ship_address_rec.name)+ '' + xml_output += '' +str(ship_address_rec.street)+ '' + xml_output += '' +str(ship_address_rec.street2)+ '' + xml_output += '' +str(ship_address_rec.city)+ '' + xml_output += '' +str(ship_address_rec.state_id.code)+ '' + xml_output += '' +str(ship_address_rec.zip)+ '' + xml_output += '' +str(ship_address_rec.country_id.code)+ '' + xml_output += 'CH' + xml_output += '' +str(cust_rec.name)+ '' + xml_output += '' +str(cust_rec.phone)+ '' + xml_output += '' +str(cust_rec.fax)+ '' + xml_output += '' +str(cust_rec.email)+ '
' + + #FOBRelatedInstruction + xml_output += ''+str(sale_obj.fob_pay_code)+'' + xml_output += '' +str(sale_obj.fob_location_qualifier)+ '' + xml_output += '' +str(sale_obj.fob_location_description)+ '' + xml_output += '' +str(sale_obj.fob_title_passage_code)+ '' + xml_output += '' +str(sale_obj.fob_title_passage_location)+ '' + + #CarrierInformation + xml_output += ''+str(sale_obj.carrier_trans_method_code)+'' + xml_output += '' +str(sale_obj.carrier_alpha_code)+ '' + xml_output += '' +str(sale_obj.carrier_routing)+ '' + xml_output += '' +str(sale_obj.routing_sequence_code)+ '' + xml_output += '' +str(sale_obj.service_leve_code)+ '' + + #Reference + xml_output += ''+str(sale_obj.reference_qual)+'' + xml_output += '' +str(sale_obj.reference_id)+ '' + xml_output += '' +str(sale_obj.ref_description)+ '' + + #Notes + xml_output += ''+str(sale_obj.note_code)+'' + xml_output += '' +str(sale_obj.note_information_field)+ '' + + #ChargesAllowances + xml_output += ''+str(sale_obj.allow_chrg_indicator)+'' + xml_output += '' +str(sale_obj.allow_chrg_code)+ '' + xml_output += '' +str(sale_obj.allow_chrg_agency_code)+ '' + xml_output += '' +str(sale_obj.allow_chrg_agency)+ '' + xml_output += '' +str(sale_obj.allow_chrg_amt)+ '' + xml_output += '' +str(sale_obj.allow_chrg_percent_qual)+ '' + xml_output += '' +str(sale_obj.allow_chrg_percent)+ '' + xml_output += '' +str(sale_obj.allow_chrg_handling_code)+ '' + xml_output += '' +str(sale_obj.reference_identification)+ '' + xml_output += '' +str(sale_obj.allow_chrg_handling_description)+ '
' + + total_lines=0 + total_qty=0 + total_weight=0 + #Create the text string for 855 order acknowledgement + for line in sale_lines: + total_lines += 1 + est_del_date = '' + est_ship_date = '' + accept_code = '' + + if line.edi_est_ship_date: + est_ship_date = datetime.strptime(line.edi_est_ship_date,'%Y-%m-%d') + est_ship_date = est_ship_date.strftime('%m/%d/%Y') + + if line.edi_line_msg == 'reject': + accept_code='CC' + + elif line.edi_line_msg == 'backorder': + accept_code='IB' + + else: + accept_code='IA' + + uom = line.product_uom.name + quantity = int(line.product_uom_qty) + total_qty+=quantity + total_weight += (line.product_id.weight * quantity) + original_po_qty = int(line.edi_line_qty) + + if uom == 'Unit(s)': + uom = 'EA' + + else: + print "***** UOM is Not EA (Each) *****" + + #line items + xml_output += '' + xml_output += '' +str(line.id)+ '' + xml_output += '' +str(line.buyer_part_number)+ '' + xml_output += '' +str(line.vendor_part_number)+ '' + xml_output += '' +str(line.consumer_package_code)+ '' + xml_output += '' +str(line.gtin)+ '' + xml_output += '' +str(line.upc_case_code)+ '' + xml_output += 'MN' + xml_output += '' +str(line.product_id.default_code)+ '' + xml_output += '' +str(uom)+ '' + xml_output += '' +str(line.edi_line_qty)+ '' + xml_output += '' +str(line.price_unit)+ '' + xml_output += '' +str(line.purchase_price_basis)+ '' + xml_output += '' +str(sale_obj.pricelist_id.currency_id.name)+ '' + xml_output += '' +str(line.product_size_code)+ '' + xml_output += '' +str(line.product_size_description)+ '' + xml_output += '' +str(line.product_color_code)+ '' + xml_output += '' +str(line.product_color_description)+ '' + xml_output += '' +str(line.product_material_code)+ '' + xml_output += '' +str(line.product_material_description)+ '' + xml_output += '' +str(line.department)+ '' + xml_output += '' +str(line.classs)+ '' + + #date + xml_output += '' +str(line.price_unit)+ '' + xml_output += '' +today+ '' + + #PriceInformation + xml_output += '' +str(line.price_unit)+ '' + xml_output += '' +str(line.price_unit)+ '' + xml_output += '' +str(quantity)+ '' + xml_output += '' +str(line.multiple_price_quantity)+ '' + xml_output += '' +str(line.class_of_trade_code)+ '' + + #ProductOrItemDescription + xml_output += '' +str(line.item_description_type)+ '' + xml_output += '' +str(line.product_characteristic_code)+ '' + xml_output += '' +str(line.agency_qualifier_code)+ '' + xml_output += '' +str(line.product_description_code)+ '' + xml_output += '' +str(line.name)+ '' + + #PhysicalDetails + xml_output += '' +str(line.pack_qualifier)+ '' + xml_output += '' +str(line.pack_value)+ '' + xml_output += '' +str(line.pack_size)+ '' + xml_output += '' +str(line.pack_uom)+ '' + xml_output += '' +str(line.packing_medium)+ '' + xml_output += '' +str(line.packing_material)+ '' + xml_output += '' +str(line.pack_weight)+ '' + xml_output += '' +str(line.pack_weight_uom)+ '' + + #Reference + xml_output += ''+str(sale_obj.reference_qual)+'' + xml_output += '' +str(sale_obj.reference_id)+ '' + xml_output += '' +str(sale_obj.ref_description)+ '' + + #Notes + xml_output += ''+str(sale_obj.note_code)+'' + xml_output += '' +str(sale_obj.note_information_field)+ '' + + #contact address details + xml_output += '
FW' + xml_output += '' +str(ship_address_rec.name)+ '' + xml_output += '' +str(ship_address_rec.street)+ '' + xml_output += '' +str(ship_address_rec.city)+ '' + xml_output += '' +str(ship_address_rec.state_id.code)+ '' + xml_output += '' +str(ship_address_rec.zip)+ '' + xml_output += '' +str(ship_address_rec.country_id.code)+ '
' + + #ChargesAllowances + xml_output += ''+str(line.allow_chrg_indicator)+'' + xml_output += '' +str(line.allow_chrg_code)+ '' + xml_output += '' +str(line.allow_chrg_agency_code)+ '' + xml_output += '' +str(line.allow_chrg_agency)+ '' + xml_output += '' +str(line.allow_chrg_amt)+ '' + xml_output += '' +str(line.allow_chrg_percent)+ '' + xml_output += '' +str(line.percent_dollar_basis)+ '' + xml_output += '' +str(line.allow_chrg_rate)+ '' + xml_output += '' +str(line.allow_chrg_qty_uom)+ '' + xml_output += '' +str(line.allow_chrg_handling_code)+ '' + xml_output += '' +str(line.allow_chrg_handling_description)+ '' + + #line item acknowledgement + xml_output += '' + xml_output += '' +str(line.edi_line_msg)+ '' + xml_output += '' +str(quantity)+ '' + xml_output += '' +str(uom)+ '' + xml_output += '002' + xml_output += '' +str(line.edi_est_ship_date)+ '' + + xml_output += 'FCP' + xml_output += '' +str(line.price_unit)+ '' + + for tax in line.tax_id: + + xml_output += 'H780' + xml_output += '' +str(1+tax.amount/100*line.price_unit)+ '' + xml_output += '' +str(tax.amount/100)+ '099990000' + + xml_output += '
' + + xml_output += '
' + + #Summary + xml_output += ''+str(sale_obj.amount_total)+'' + xml_output += '' +str(total_lines)+ '' + xml_output += '' +str(total_qty)+ '' + xml_output += '' +str(total_weight)+ '' + xml_output += '0' + xml_output += '0' + xml_output += '0' + + date_format = "%Y-%m-%d %H:%M:%S" + now = datetime.now() + today = now.strftime(date_format) + sale_obj.write({'855_check':True, '855_sent_timestamp':today}) + print "***** SUCCESSFULLY STORED *****" + + xml_output += '
' + + #Write ASN doc to text file + filename = '855_' + today + '%s.txt' % num + filename.replace('/', '_') + fd = open(OUT_PATH + '/' + filename, 'w') + fd.write(xml_output) + fd.close() + + return True + + def _create_855_wrapper(self, cr, uid, context=None): + + #search for invoices that are ack_yes = True, edi_yes = True and 855_sent_timestamp = False + eligible_orders = self.search(cr, uid, [('ack_yes','=',True),('edi_yes','=',True),('855_sent_timestamp','=',False)], context=context) + + return eligible_orders and self.create_855(cr, uid, eligible_orders, context=context) or False + + def action_send_855(self, cr, uid, ids, context=None): + """ Creates and new 855 and puts it into the outbox + """ + if context is None: + context = {} + + #execute the create_855 method + self.create_855(cr, uid, ids, context=None) + + return True + + @api.multi + def _prepare_order_line_procurement(self, group_id=False): + + self.ensure_one() + res = super(SaleOrder, self)._prepare_order_line_procurement(group_id=group_id) + + for sale_line in self.order_line: + + res['po_number'] = sale_line.po_number or '' + res['asn_shipment'] = sale_line.asn_shipment or '' + res['sale_line_id'] = sale_line.id or False + res['edi_line_num'] = sale_line.edi_line_num or 0 + res['so_id'] = self.id or False + res['ship_to_code'] = self.ship_to_code or '' + res['trading_partner_id'] = self.trading_partner_id and self.trading_partner_id.id or False + res['edi_yes'] = self.trading_partner_id and self.edi_yes or False + res['ship_not_before_date'] = self.trading_partner_id and self.ship_not_before_date or False + res['cancel_after_date'] = self.trading_partner_id and self.cancel_after_date or False + + return res + + @api.model + def _prepare_procurement_group(self): + + res = super(SaleOrder, self)._prepare_procurement_group() + res['trading_partner_id'] = self.trading_partner_id and self.trading_partner_id.id or False + #res['edi_yes'] = self.trading_partner_id and self.edi_yes or False + res['edi_yes'] = True + res['asn_shipment'] = self.asn_shipment or 'asn_id' + res['ship_to_code'] = self.ship_to_code or '' + res['ship_not_before_date'] = self.trading_partner_id and self.ship_not_before_date or False + res['cancel_after_date'] = self.trading_partner_id and self.cancel_after_date or False + res['po_number'] = self.client_order_ref or '' + + return res + + @api.multi + def test_edi_status(self): + + for line in self.order_line: + + if line.edi_yes and not line.edi_line_msg and line.ack_yes: + return False + + return True + + @api.multi + def _prepare_invoice(self): + """ + Prepare the dict of values to create the new invoice for a sales order. This method may be + overridden to implement custom invoice generation (making sure to call super() to establish + a clean extension chain). + """ + stock_picking_obj=self.pool['stock.picking'] + pick = False + + for pick in self.picking_ids: + + if pick.picking_type_id.id==2: + + break + + res = super(SaleOrder, self)._prepare_invoice() + res['sale_id'] = self.id or False + res['bol_num'] = self.picking_ids and pick and pick.bol_num or self.bol_num or '' + res['picking_ids'] = self.picking_ids + res['scac_code'] = self.picking_ids and pick and pick.carrier_id.scac_code or self.scac_code or False + res['tracking_num'] = self.picking_ids and pick and pick.tracking_number or '' + res['sender_id'] = self.partner_id.sender_id or '' + res['asn_shipment'] = pick and pick.name or '' + res['trading_partner_id'] = self.trading_partner_id.id or False + res['edi_yes'] = self.edi_yes or False + res['ship_not_before_date'] = self.ship_not_before_date or False + res['cancel_after_date'] = self.cancel_after_date or False + res['supplier_code'] = self.supplier_code or False + res['ship_to_code'] = self.ship_to_code or False + res['client_order_ref'] = self.client_order_ref or False + res['tset_purpose_code'] = self.tset_purpose_code or '' + res['purchase_order_type_code'] = self.purchase_order_type_code or '' + res['po_type_description'] = self.po_type_description or '' + res['ship_complete_code'] = self.ship_complete_code or '' + res['department'] = self.department or '' + res['division'] = self.division or '' + res['promotion_deal_number'] = self.promotion_deal_number or '' + res['terms_type'] = self.terms_type or '' + res['terms_basis_date_code'] = self.terms_basis_date_code or '' + res['terms_discount_percentage'] = self.terms_discount_percentage or '' + res['terms_discount_due_days'] = self.terms_discount_due_days or '' + res['terms_net_due_days'] = self.terms_net_due_days or '' + res['payment_method_code'] = self.payment_method_code or '' + res['fob_pay_code'] = self.fob_pay_code or '' + res['fob_location_qualifier'] = self.fob_location_qualifier or '' + res['fob_location_description'] = self.fob_location_description or '' + res['fob_title_passage_code'] = self.fob_title_passage_code or '' + res['fob_title_passage_location'] = self.fob_title_passage_location or '' + res['carrier_trans_method_code'] = self.carrier_trans_method_code or '' + res['carrier_alpha_code'] = self.carrier_alpha_code or '' + res['carrier_routing'] = self.carrier_routing or '' + res['routing_sequence_code'] = self.routing_sequence_code or '' + res['service_level_code'] = self.service_level_code or '' + res['reference_qual'] = self.reference_qual or '' + res['reference_id'] = self.reference_id or '' + res['ref_description'] = self.ref_description or '' + res['note_code'] = self.note_code or '' + res['note_information_field'] = self.note_information_field or '' + res['allow_chrg_indicator'] = self.allow_chrg_indicator or '' + res['allow_chrg_code'] = self.allow_chrg_code or '' + res['allow_chrg_agency_code'] = self.allow_chrg_agency_code or '' + res['allow_chrg_agency'] = self.allow_chrg_agency or '' + res['allow_chrg_amt'] = float(self.allow_chrg_amt) or 0 + res['allow_chrg_percent_qual'] = self.allow_chrg_percent_qual or '' + res['allow_chrg_percent'] = float(self.allow_chrg_percent) or 0 + res['allow_chrg_handling_code'] = self.allow_chrg_handling_code or '' + res['reference_identification'] = self.reference_identification or '' + res['allow_chrg_handling_description'] = self.allow_chrg_handling_description or '' + return res + + +class SaleOrderLine(osv.osv): + _inherit = "sale.order.line" + + @api.multi + def _prepare_invoice_line(self, qty): + """ + Prepare the dict of values to create the new invoice line for a sales order line. + + :param qty: float quantity to invoice + """ + res = super(SaleOrderLine, self)._prepare_invoice_line(qty=qty) + res['edi_line_qty'] = self.edi_line_qty + res['edi_line_num'] = self.edi_line_num + res['asn_shipment'] = self.asn_shipment + res['po_number'] = self.po_number + res['buyer_part_number'] = self.buyer_part_number + res['vendor_part_number'] = self.vendor_part_number or '' + res['consumer_package_code'] = self.consumer_package_code or '' + res['gtin'] = self.gtin or '' + res['upc_case_code'] = self.upc_case_code or '' + res['purchase_price_basis'] = self.purchase_price_basis or '' + res['product_size_code'] = self.product_size_code or '' + res['product_size_description'] = self.product_size_description or '' + res['product_color_code'] = self.product_color_code or '' + res['product_color_description'] = self.product_color_description or '' + res['product_material_code'] = self.product_material_code or '' + res['product_material_description'] = self.product_material_description or '' + res['department'] = self.department or '' + res['classs'] = self.classs or '' + res['price_type_id_code'] = self.price_type_id_code or '' + res['multiple_price_quantity'] = self.multiple_price_quantity or '' + res['class_of_trade_code'] = self.class_of_trade_code or '' + res['item_description_type'] = self.item_description_type or '' + res['product_characteristic_code'] = self.product_characteristic_code or '' + res['agency_qualifier_code'] = self.agency_qualifier_code or '' + res['product_description_code'] = self.product_description_code or '' + res['pack_qualifier'] = self.pack_qualifier or '' + res['pack_value'] = self.pack_value or '' + res['pack_size'] = self.pack_size or '' + res['pack_uom'] = self.pack_uom or '' + res['packing_medium'] = self.packing_medium or '' + res['packing_material'] = self.packing_material or '' + res['pack_weight'] = self.pack_weight or '' + res['pack_weight_uom'] = self.pack_weight_uom or '' + res['location_code_qualifier'] = self.location_code_qualifier or '' + res['location'] = self.location or '' + res['allow_chrg_indicator'] = self.allow_chrg_indicator or '' + res['allow_chrg_code'] = self.allow_chrg_code or '' + res['allow_chrg_agency_code'] = self.allow_chrg_agency_code or '' + res['allow_chrg_agency'] = self.allow_chrg_agency or '' + res['allow_chrg_amt'] = float(self.allow_chrg_amt) or 0 + res['allow_chrg_percent'] = self.allow_chrg_percent or '' + res['percent_dollar_basis'] = self.percent_dollar_basis or '' + res['allow_chrg_rate'] = self.allow_chrg_rate or '' + res['allow_chrg_handling_code'] = self.allow_chrg_handling_code or '' + res['allow_chrg_qty_uom'] = self.allow_chrg_qty_uom or '' + res['allow_chrg_handling_description'] = self.allow_chrg_handling_description or '' + return res + + def edi_line_status_change(self, cr, uid, ids, order_id, edi_line_msg, order_edi_last_date, context=None): + res = {'value': {'state':'draft', 'edi_est_ship_date': order_edi_last_date}} + if ids: + if edi_line_msg == 'reject': + res['value']['state'] = 'cancel' + res['value']['edi_est_ship_date'] = '' + return res + + _columns = { + 'edi_yes': fields.boolean('From an EDI PO?', help="Is this order from an EDI purchase order, 850 EDI doc?"), + 'asn_shipment': fields.char('ASN Shipment Number from Converted 856'), + 'po_number': fields.char('Line Item PO Number from Converted 856'), + 'buyer_part_number': fields.char(string='Buyer Part Number'), + 'edi_line_num': fields.integer('EDI PO line number'), + 'edi_line_qty': fields.float('Original EDI Quantity', digits_compute=dp.get_precision('Product Unit of Measure')), + 'ack_yes': fields.boolean('855', readonly=True, help="Will this order have an 855?"), + 'edi_est_del_date': fields.date('Est. Del. Date', help="Line Item Estimated Delivery Date"), + 'edi_est_ship_date': fields.date('Est. Ship Date', help="Line Item Estimated Shipping Date"), + 'edi_line_msg': fields.selection((('reject','Reject'), ('accept','Accept'), ('backorder','Backorder')),'EDI Line Status'), + 'ship_not_before_date': fields.date('Do Not Ship Before This Date', help="Do Not Ship Before This Date."), + 'cancel_after_date': fields.date('Cancel if Shipped After This Date', help="Cancel if Shipped After This Date."), + 'trading_partner_id': fields.many2one('edi.config', 'Trading Partner', help='EDI Configuration information for partner'), + 'edi_intransit_qty': fields.float('Incoming Qty', digits_compute=dp.get_precision('Product Unit of Measure')), + 'edi_outgoing_qty': fields.float('Reserved Qty', digits_compute=dp.get_precision('Product Unit of Measure')), + 'edi_avlforsale_qty': fields.float('Available for Sale', digits_compute=dp.get_precision('Product Unit of Measure')), + 'vendor_part_number': fields.char('VendorPartNumber'), + 'consumer_package_code': fields.char('ConsumerPackageCode'), + 'gtin': fields.char('GTIN'), + 'upc_case_code': fields.char('UPCCaseCode'), + 'purchase_price_basis': fields.char('PurchasePriceBasis'), + 'product_size_code': fields.char('ProductSizeCode'), + 'product_size_description': fields.char('ProductSizeDescription'), + 'product_color_code': fields.char('ProductColorCode'), + 'product_color_description': fields.char('ProductColorDescription'), + 'product_material_code': fields.char('ProductMaterialCode'), + 'product_material_description': fields.char('ProductMaterialDescription'), + 'department': fields.char('Department'), + 'classs': fields.char('Class'), + 'price_type_id_code': fields.char('PriceTypeIDCode'), + 'multiple_price_quantity': fields.float('MultiplePriceQuantity'), + 'class_of_trade_code': fields.char('ClassOfTradeCode'), + 'item_description_type': fields.char('ItemDescriptionType'), + 'product_characteristic_code': fields.char('ProductCharacteristicCode'), + 'agency_qualifier_code': fields.char('AgencyQualifierCode'), + 'product_description_code': fields.char('ProductDescriptionCode'), + 'pack_qualifier': fields.char('PackQualifier'), + 'pack_value': fields.integer('PackValue'), + 'pack_size': fields.char('PackSize'), + 'pack_uom': fields.char('PackUOM'), + 'packing_medium': fields.char('PackingMedium'), + 'packing_material': fields.char('PackingMaterial'), + 'pack_weight': fields.float('PackWeight'), + 'pack_weight_uom': fields.char('PackWeightUOM'), + 'location_code_qualifier':fields.char('LocationCodeQualifier', help="Code identifying the structure or format of the related location number(s)."), + 'location':fields.char('Location', help="For CrossDock, it's the marked for location. For MultiStore[could also be DC] ship-to location."), + 'allow_chrg_indicator':fields.char('AllowChrgIndicator', help="Code which indicates an allowance or charge for the service specified."), + 'allow_chrg_code':fields.char('AllowChrgCode', help="Code describing the type of allowance or charge for the service specified."), + 'allow_chrg_agency_code':fields.char('AllowChrgAgencyCode', help="Code identifying the agency assigning the code values."), + 'allow_chrg_agency':fields.char('AllowChrgAgency', help="Agency maintained code identifying the service, promotion, allowance, or charge."), + 'allow_chrg_amt':fields.float('AllowChrgAmt', help="Amount of the allowance or charge."), + 'allow_chrg_percent':fields.float('AllowChrgPercent', help="Percentage of allowance or charge. Percentages should be represented as real numbers[0% through 100% should be normalized to 0.0 through 100.00].."), + 'percent_dollar_basis':fields.float('PercentDollarBasis', help="."), + 'allow_chrg_rate':fields.float('AllowChrgRate', help="Amount of the allowance or charge."), + 'allow_chrg_handling_code':fields.char('AllowChrgHandlingCode', help="Code indicating method of handling for an allowance or charge.."), + 'allow_chrg_qty_uom':fields.char('AllowChrgQtyUOM', help=""), + 'allow_chrg_handling_description':fields.char('AllowChrgHandlingDescription', help="Free-form textual description of the note."), + } + + def _get_commitment_date(self, cr, uid, line, context=None): + """Compute the estimated delivery date""" + order = line.order_id[0] + order_datetime = datetime.strptime(order.date_order, DEFAULT_SERVER_DATETIME_FORMAT) + dt = order_datetime + timedelta(days=line.delay or 0.0) + est_del_date = dt.strftime(DEFAULT_SERVER_DATETIME_FORMAT) + return est_del_date \ No newline at end of file diff --git a/connector_spscommerce/models/stock.py b/connector_spscommerce/models/stock.py new file mode 100644 index 0000000..70e4c57 --- /dev/null +++ b/connector_spscommerce/models/stock.py @@ -0,0 +1,657 @@ +# -*- coding: utf-8 -*- +# Copyright (c) Open Source Integrators +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from datetime import datetime +import re +import dicttoxml +from openerp.osv import fields, osv +from openerp.tools.misc import DEFAULT_SERVER_DATETIME_FORMAT, DEFAULT_SERVER_DATE_FORMAT + + +class product_pricelist(osv.osv): + _inherit= 'product.pricelist' + + def price_get_wrapper(self, cr, uid, ids, prod_id, qty, partner=None, context=None): + price = self.price_rule_get(cr, uid, ids, prod_id, qty, partner=partner, context=context)[ids][0] + return price + + +class stock_pack_operation(osv.osv): + _inherit = "stock.pack.operation" + + _columns = { + 'tracking_number': fields.char('Tracking Number', help="Tracking Number"), + 'bol': fields.char('Bill of Lading Number', help="Bill of Lading Number"), + 'package_code': fields.selection((('PLT71', 'PLT71'), ('CTN25', 'CTN25')), 'Package Code', help="Pkg Code Qualifier.", default="PLT71"), + 'po_number': fields.char('PO Number from EDI 850', help="PO Number from EDI 850."), + 'edi_line_num': fields.char('Line Number from EDI 850', help="Line Number from EDI 850."), + 'reference_qual':fields.char('ReferenceQual', help="Code specifying the type of data in the ReferenceID/ReferenceDescription."), + 'reference_id':fields.char('ReferenceID', help="Code specifying the type of data in the ReferenceID/ReferenceDescription."), + 'ref_description':fields.char('Description', help="Free-form textual description to clarify the related data elements and their content."), + 'note_code':fields.char('NoteCode', help="Code specifying the type of note."), + 'note_information_field':fields.char('NoteInformationField', help="Free-form textual description of the note."), + 'pack_qualifier':fields.char('PackQualifier', help=""), + 'pack_value':fields.char('PackValue', help=""), + 'pack_size':fields.char('PackSize', help=""), + 'pack_uom':fields.char('PackUOM', help=""), + 'packing_medium':fields.char('PackingMedium', help=""), + 'packing_material':fields.char('PackingMaterial', help=""), + } + + +class stock_quant(osv.osv): + _inherit = "stock.quant" + + _columns = { + 'tracking_number': fields.char('Tracking Number', help="Tracking Number"), + 'bol': fields.char('Bill of Lading Number', help="Bill of Lading Number"), + 'package_code': fields.selection((('PLT71', 'PLT71'), ('CTN25', 'CTN25')), 'Package Code', help="Pkg Code Qualifier.", default="PLT71"), + } + + +class stock_quant_package(osv.osv): + _inherit = "stock.quant.package" + + _columns = { + 'tracking_number': fields.char('Tracking Number', help="Tracking Number"), + 'bol': fields.char('Bill of Lading Number', help="Bill of Lading Number"), + 'package_code': fields.selection((('PLT71', 'PLT71'), ('CTN25', 'CTN25')), 'Package Code', help="Pkg Code Qualifier.", default="PLT71"), + } + + +class delivery_carrier(osv.osv): + _inherit = "delivery.carrier" + + _columns = { + 'scac_code': fields.char('SCAC Ship Method Code', help="Shipping carrier code."), + } + + +class stock_move(osv.osv): + _inherit = "stock.move" + + _columns = { + 'product_material_description': fields.char('ProductMaterialDescription', help="ProductMaterialDescription"), + 'consumer_package_code': fields.char('ConsumerPackageCode', help="ConsumerPackageCode"), + 'gtin': fields.char('GTIN', help="GTIN"), + 'upc_case_code': fields.char('UPCCaseCode', help="UPCCaseCode"), + 'natl_drug_code': fields.char('NatlDrugCode', help="NatlDrugCode"), + 'international_standard_book_number': fields.char('InternationalStandardBookNumber', help="InternationalStandardBookNumber"), + 'product_size_description': fields.char('ProductSizeDescription', help="ProductSizeDescription"), + 'product_color_description': fields.char('ProductColorDescription', help="ProductColorDescription"), + } + + +class stock_picking(osv.osv): + _inherit = "stock.picking" + + _columns = { + 'ship_not_before_date': fields.date('Do Not Ship Before This Date', help="Do Not Ship Before This Date."), + 'cancel_after_date': fields.date('Cancel if Shipped After This Date', help="Cancel if Shipped After This Date."), + 'client_order_ref': fields.text('Customer PO #', help="Customer PO #"), + 'edi_yes': fields.boolean('From an EDI PO', readonly=True, help="Is this order from an EDI purchase order, 850 EDI doc."), + 'est_del_date': fields.date('Estimated Delivery Date', help="Calculated based on shipping method."), + 'trading_partner_id': fields.many2one('edi.config', 'Trading Partner', help='EDI Configuration information for partner'), + '856_sent_timestamp': fields.datetime('856 Sent Date', help="The timestamp for when the 856 was sent."), + '856_check': fields.boolean('856 Created', help="A check to see if 856 has been sent."), + 'bol_num': fields.char('BoL', help="BoL Number."), + 'tracking_number': fields.char('Tracking Number', help="Tracking Number"), + 'ship_to_code': fields.char('Ship To Warehouse', help="Trading Partner Ship to location code."), + 'package_code': fields.selection((('PLT71', 'PLT71'), ('CTN25', 'CTN25')), 'Package Code', help="Pkg Code Qualifier.", default="PLT71"), + 'tset_purpose_code':fields.char('TsetPurposeCode', help="Code identifying purpose of the document.."), + 'purchase_order_type_code':fields.char('PurchaseOrderTypeCode', help="Code specifying the type of purchase order."), + 'po_type_description':fields.char('POTypeDescription', help="Free form text to describe the type of order."), + 'ship_complete_code':fields.char('ShipCompleteCode', help="Code to identify a specific requirement or agreement of sale. Should only be used to indicate if an item can be placed on backorder."), + 'department':fields.char('Department', help="Name or number identifying an area wherein merchandise is categorized within a store."), + 'division':fields.char('Division', help="Different entities belonging to the same parent company."), + 'promotion_deal_number':fields.char('PromotionDealNumber', help="Number uniquely identifying an agreement for a special offer or price."), + 'terms_type':fields.char('TermsType', help="Code identifying type of payment terms."), + 'terms_basis_date_code':fields.char('TermsBasisDateCode', help="Code identifying the beginning of the terms period."), + 'terms_discount_percentage':fields.char('TermsDiscountPercentage', help="Terms discount percentage available to the purchaser"), + 'terms_discount_due_days':fields.char('TermsDiscountDueDays', help="Number of days by which payment or invoice must be received in order to receive the discount noted."), + 'terms_net_due_days':fields.char('TermsNetDueDays', help="Number of days until total invoice amount is due[discount not applicable."), + 'payment_method_code':fields.char('PaymentMethodCode', help="Indication of the instrument of payment."), + 'fob_pay_code':fields.char('FOBPayCode', help="Code identifying payment terms for transportation charges."), + 'fob_location_qualifier':fields.char('FOBLocationQualifier', help="Code identifying type of location at which ownership of goods is transferred."), + 'fob_location_description':fields.char('FOBLocationDescription', help="Free-form textual description of the location at which ownership of goods is transferred."), + 'fob_title_passage_code':fields.char('FOBTitlePassageCode', help="Code describing the location of ownership of the goods."), + 'fob_title_passage_location':fields.char('FOBTitlePassageLocation', help="Location of ownership of the goods."), + 'carrier_trans_method_code':fields.char('CarrierTransMethodCode', help="Code specifying the method or type of transportation for the shipment."), + 'carrier_alpha_code':fields.char('CarrierAlphaCode', help="Standard Carrier Alpha Code[SCAC] - "), + 'carrier_routing':fields.char('CarrierRouting', help="Free-form description of the routing/requested routing for shipment or the originating carrier's identity."), + 'routing_sequence_code':fields.char('RoutingSequenceCode', help=""), + 'service_level_code':fields.char('ServiceLevelCode', help="Code indicating the level of transportation service or the billing service offered by the transportation carrier."), + 'reference_qual':fields.char('ReferenceQual', help="Code specifying the type of data in the ReferenceID/ReferenceDescription."), + 'reference_id':fields.char('ReferenceID', help="Code specifying the type of data in the ReferenceID/ReferenceDescription."), + 'ref_description':fields.char('Description', help="Free-form textual description to clarify the related data elements and their content."), + 'note_code':fields.char('NoteCode', help="Code specifying the type of note."), + 'note_information_field':fields.char('NoteInformationField', help="Free-form textual description of the note."), + 'allow_chrg_indicator':fields.char('AllowChrgIndicator', help="Code which indicates an allowance or charge for the service specified."), + 'allow_chrg_code':fields.char('AllowChrgCode', help="Code describing the type of allowance or charge for the service specified."), + 'allow_chrg_agency_code':fields.char('AllowChrgAgencyCode', help="Code identifying the agency assigning the code values."), + 'allow_chrg_agency':fields.char('AllowChrgAgency', help="Agency maintained code identifying the service, promotion, allowance, or charge."), + 'allow_chrg_amt':fields.float('AllowChrgAmt', help="Amount of the allowance or charge."), + 'allow_chrg_percent_qual':fields.char('AllowChrgPercentQual', help="Code indicating on what basis an allowance or charge percent is calculated.."), + 'allow_chrg_percent':fields.float('AllowChrgPercent', help="Percentage of allowance or charge. Percentages should be represented as real numbers[0% through 100% should be normalized to 0.0 through 100.00].."), + 'allow_chrg_handling_code':fields.char('AllowChrgHandlingCode', help="Code indicating method of handling for an allowance or charge.."), + 'reference_identification':fields.char('ReferenceIdentification', help=""), + 'allow_chrg_handling_description':fields.char('AllowChrgHandlingDescription', help="Free-form textual description of the note."), + 'appointment_number':fields.char('AppointmentNumber', help=""), + 'asn_structure_code':fields.char('ASNStructureCode', help=""), + 'address_type_code':fields.char('AddressTypeCode', help=""), + 'location_code_qualifier':fields.char('LocationCodeQualifier', help=""), + 'address_location_number':fields.char('AddressLocationNumber', help=""), + 'status_code':fields.char('StatusCode', help=""), + 'equipment_description_code':fields.char('EquipmentDescriptionCode', help=""), + 'carrier_equipment_initial':fields.char('CarrierEquipmentInitial', help=""), + 'carrier_equipment_number':fields.char('CarrierEquipmentNumber', help=""), + 'seal_number':fields.char('SealNumber', help=""), + 'allow_chrg_rate':fields.char('AllowChrgRate', help=""), + } + + def put_in_pack(self, cr, uid, ids, context=None): + + stock_move_obj = self.pool["stock.move"] + stock_operation_obj = self.pool["stock.pack.operation"] + stock_move_op_link_obj = self.pool['stock.move.operation.link'] + package_obj = self.pool["stock.quant.package"] + package_id = False + context = context or {} + + for pick in self.browse(cr, uid, ids, context=context): + operations = [x for x in pick.pack_operation_ids] + + for operation in operations: + + #assign edi_line_num and po_number to pack operation, move by move. + link_ids = stock_move_op_link_obj.search(cr, uid, [('operation_id','=',operation.id)], context=context) or [] + move = False + + for link_id in link_ids: + + link = stock_move_op_link_obj.browse(cr, uid, link_id, context=context) + + if link and link.move_id: + + move = link.move_id + break + + op_write = move and link and link.operation_id and stock_operation_obj.write(cr, uid, [link.operation_id.id], {'edi_line_num': move.edi_line_num, 'po_number':move.po_number}, context=context) or False + + return super(stock_picking, self).put_in_pack(cr, uid, ids, context=context) + + def create(self, cr, uid, vals, context=None): + + context = context or {} + res = super(stock_picking, self).create(cr, uid, vals, context=context) + picking = self.browse(cr, uid, res, context=context) + + + + if picking.origin: + + sale_obj = self.pool['sale.order'] + sale_id = sale_obj.search(cr, uid, [('name','=',picking.origin)], context=context) + + #look back to the sale order and get the edi field values and write them to the picking + if sale_id: + + sale_order = sale_obj.browse(cr, uid, sale_id[0], context=context) + data = { + 'ship_not_before_date': sale_order.ship_not_before_date, + 'cancel_after_date': sale_order.cancel_after_date, + 'client_order_ref': sale_order.client_order_ref, + 'edi_yes': sale_order.edi_yes, + 'trading_partner_id': sale_order.trading_partner_id.id, + 'bol_num': sale_order.bol_num, + #'tracking_number': sale_order.tracking_number, + 'scac_code': sale_order.scac_code, + 'ship_to_code': sale_order.ship_to_code, + } + + picking.write(data) + + return res + + def _get_invoice_vals(self, cr, uid, key, inv_type, journal_id, move, context=None): + res = super(stock_picking, self)._get_invoice_vals(cr, uid, key, inv_type, journal_id, move, context) + + if context is None: + context = {} + + res['so_id'] = move.picking_id.sale_id or False + res['trading_partner_id'] = move.picking_id.trading_partner_id.id or False + res['edi_yes'] = move.picking_id.edi_yes or False + res['ship_not_before_date'] = move.picking_id.ship_not_before_date or False + res['cancel_after_date'] = move.picking_id.cancel_after_date or False + res['bol_num'] = move.picking_id.bol_num or '' + res['tracking_number'] = move.picking_id.carrier_tracking_ref or '' + res['scac_code'] = move.picking_id.carrier_id.scac_code or '' + res['ship_to_code'] = move.picking_id.ship_to_code or '' + return res + + def get_operations(self, cr, uid, picking_ids, context=None): + + operations_dict = {} + stock_move_op_link_obj = self.pool['stock.move.operation.link'] + operation_obj = self.pool['stock.pack.operation'] + + for picking_id in picking_ids: + + #find links between this move and pack operations + pack_op_ids = operation_obj.search(cr, uid, [('picking_id', '=', picking_id)], context=context) + operations = operation_obj.browse(cr, uid, pack_op_ids, context=context) + operations_dict[picking_id] = operations + + #loop through quants found in packages for this particular move + for operation in operations: + + if not operation.result_package_id: + continue + + #assign edi_line_num and po_number to pack operation, move by move. + link_id = operation.qty_done and stock_move_op_link_obj.search(cr, uid, [('operation_id','=',operation.id)], context=context) or [] + move = link_id and stock_move_op_link_obj.browse(cr, uid, link_id, context=context).move_id or False + + package = operation.result_package_id + + return operations_dict + + def create_text_856(self, cr, uid, picking_ids, context=None): + + today = str(datetime.now().strftime('%Y%m%d')) or '' + processed = 0 + name = '' + # for each picking + shipments_list = {'Shipments':[]} + for picking in self.browse(cr, uid, picking_ids, context=context): + name += picking.name + + #Grab the vendor_id and trading_partner_id for EDI in the edi_config. + trading_partner_code = picking.trading_partner_id.partner_header_string + vendor_code = picking.trading_partner_id.vendor_header_string + + #ship_date + ship_date = picking.date_done + dateship_object = datetime.strptime(ship_date, DEFAULT_SERVER_DATETIME_FORMAT) + ship_date = dateship_object.date() + ship_date = str(ship_date) + + #ship_time + ship_time = picking.date_done + dateship_object = datetime.strptime(ship_time, DEFAULT_SERVER_DATETIME_FORMAT) + ship_time = dateship_object.time() + ship_time = str(ship_time) + + #Schedule_date + schedule_date = picking.est_del_date + if not schedule_date: + schedule_date = picking.max_date + dateship_object = datetime.strptime(schedule_date, DEFAULT_SERVER_DATETIME_FORMAT) + else: + dateship_object = datetime.strptime(schedule_date, DEFAULT_SERVER_DATE_FORMAT) + schedule_date = dateship_object.date() + schedule_date = str(schedule_date) + + #Schedule_time + schedule_time = picking.est_del_date + if not schedule_time: + schedule_time = picking.max_date + dateship_object = datetime.strptime(schedule_time, DEFAULT_SERVER_DATETIME_FORMAT) + else: + dateship_object = datetime.strptime(schedule_time, DEFAULT_SERVER_DATE_FORMAT) + schedule_time = dateship_object.time() + schedule_time = str(schedule_time) + + #Notice_date + notice_date = picking.ship_not_before_date + if not notice_date: + notice_date = picking.min_date + dateship_object = datetime.strptime(notice_date, DEFAULT_SERVER_DATETIME_FORMAT) + else: + dateship_object = datetime.strptime(notice_date, DEFAULT_SERVER_DATE_FORMAT) + notice_date = dateship_object.date() + notice_date = str(notice_date) + + # get total packages and weight for the entire order + total_weight = 0 + total_qty = 0 + + for move in picking.move_lines: + total_qty += move.product_qty + total_weight += move.product_id.weight * move.product_qty + + # initialize + shipment_dict = {'Shipment':{}} + # 1. Header Line - one line for the order + meta_dict = {'Meta': {'Version': '1.0'}} + header_dict = {'Header': {'ShipmentHeader': { + 'TradingPartnerId': trading_partner_code, + 'ShipmentIdentification': str(picking.name), + 'ShipmentDate':ship_date, + 'TsetPurposeCode':picking.tset_purpose_code, + 'ShipNoticeDate':notice_date, + 'ShipNoticeTime':'00:00:00', + 'ASNStructureCode':picking.asn_structure_code, + 'BillOfLadingNumber':picking.bol_num, + 'CarrierProNumber':picking.carrier_tracking_ref, + 'AppointmentNumber':picking.appointment_number, + 'CurrentScheduledDeliveryDate':schedule_date, + 'CurrentScheduledDeliveryTime':schedule_time, + }, + 'Date': { + 'DateTimeQualifier1':'945', + 'Date1':ship_date, + 'Time1':ship_time, + 'DateTimePeriod':'', + }, + 'Reference':{ + 'ReferenceQual':picking.reference_qual, + 'ReferenceID':picking.reference_id, + 'Description':picking.ref_description, + }, + 'Notes':{ + 'NoteCode':picking.note_code, + 'NoteInformationField':picking.note_information_field, + }, + 'Contact':{ + 'ContactTypeCode':'BD', + 'ContactName':picking.partner_id and picking.partner_id.name or '', + 'PrimaryPhone':picking.partner_id and picking.partner_id.phone or '', + 'PrimaryFax':picking.partner_id and picking.partner_id.fax or '', + 'PrimaryEmail':picking.partner_id and picking.partner_id.email or '', + }, + 'Address':{ + 'AddressTypeCode':picking.address_type_code, + 'LocationCodeQualifier':picking.location_code_qualifier, + 'AddressLocationNumber':picking.address_location_number, + 'AddressName':picking.partner_id.name, + 'Address1':picking.partner_id.street, + 'Address2':picking.partner_id.street2, + 'City':picking.partner_id.city, + 'State':picking.partner_id.state_id.name, + 'PostalCode':picking.partner_id.zip, + 'Country':picking.partner_id.country_id.name, + 'Contact':{ + 'ContactTypeCode':'BD', + 'ContactName':picking.partner_id and picking.partner_id.name or '', + 'PrimaryPhone':picking.partner_id and picking.partner_id.phone or '', + 'PrimaryFax':picking.partner_id and picking.partner_id.fax or '', + 'PrimaryEmail':picking.partner_id and picking.partner_id.email or '', + }, + }, + 'CarrierInformation':{ + 'StatusCode':picking.status_code, + 'CarrierTransMethodCode':picking.carrier_trans_method_code, + 'CarrierAlphaCode':picking.carrier_alpha_code, + 'CarrierRouting':picking.carrier_routing, + 'EquipmentDescriptionCode':picking.equipment_description_code, + 'CarrierEquipmentInitial':picking.carrier_equipment_initial, + 'CarrierEquipmentNumber':picking.carrier_equipment_number, + 'SealNumber':picking.seal_number, + 'RoutingSequenceCode': picking.routing_sequence_code + }, + 'QuantityAndWeight':{ + 'LadingQuantity':total_qty, + 'WeightQualifier':'G', + 'Weight':total_weight, + 'WeightUOM':'AD', + }, + 'ChargesAllowances':{ + 'AllowChrgIndicator':picking.allow_chrg_indicator, + 'AllowChrgCode':picking.allow_chrg_code, + 'AllowChrgAmt':picking.allow_chrg_amt, + 'AllowChrgPercentQual':picking.allow_chrg_percent_qual, + 'AllowChrgPercent':picking.allow_chrg_percent, + 'AllowChrgHandlingCode':picking.allow_chrg_handling_code, + 'AllowChrgHandlingDescription':picking.allow_chrg_handling_description, + 'AllowChrgRate':picking.allow_chrg_rate + }, + 'FOBRelatedInstruction':{ + 'FOBPayCode':picking.fob_pay_code, + 'FOBLocationQualifier':picking.fob_location_qualifier, + 'FOBLocationDescription':picking.fob_location_description, + }, + } + } + orderlevel_dict = {'OrderLevel': {'OrderHeader': { + 'InternalOrderNumber': picking.name, + 'InternalOrderDate': ship_date, + 'InvoiceNumber':'99999-123', + 'InvoiceDate':'', + 'PurchaseOrderNumber':picking.origin, + 'ReleaseNumber':picking.name, + 'PurchaseOrderDate':ship_date, + 'Department':picking.department, + 'Vendor':picking.vendor, + 'CustomerOrderNumber':'' + }, + 'QuantityAndWeight':{ + 'LadingQuantity':total_qty, + 'WeightQualifier':'G', + 'Weight':total_weight, + 'WeightUOM':'AD', + }, + 'CarrierInformation':{ + 'StatusCode':picking.status_code, + 'CarrierTransMethodCode':picking.carrier_trans_method_code, + 'CarrierAlphaCode':picking.carrier_alpha_code, + 'CarrierRouting':picking.carrier_routing, + 'EquipmentDescriptionCode':picking.equipment_description_code, + 'CarrierEquipmentInitial':picking.carrier_equipment_initial, + 'CarrierEquipmentNumber':picking.carrier_equipment_number, + 'SealNumber':picking.seal_number, + 'RoutingSequenceCode': picking.routing_sequence_code + }, + 'Reference':{ + 'ReferenceQual':picking.reference_qual, + 'ReferenceID':picking.reference_id, + 'Description':picking.ref_description, + }, + 'Notes':{ + 'NoteCode':picking.note_code, + 'NoteInformationField':picking.note_information_field, + }, + 'ChargesAllowances':{ + 'AllowChrgIndicator':picking.allow_chrg_indicator, + 'AllowChrgCode':picking.allow_chrg_code, + 'AllowChrgAmt':picking.allow_chrg_amt, + 'AllowChrgPercentQual':picking.allow_chrg_percent_qual, + 'AllowChrgPercent':picking.allow_chrg_percent, + 'AllowChrgHandlingCode':picking.allow_chrg_handling_code, + 'AllowChrgHandlingDescription':picking.allow_chrg_handling_description, + 'AllowChrgRate':picking.allow_chrg_rate + }, + } + } + packlevel_dict = {'PackLevel':{}, 'Itemlevel':{'ShipmentLine':{}}} + + for operation in self.get_operations(cr, uid, picking_ids, context=context): + + packlevel_dict['PackLevel'] = {'Pack':{ + 'PackLevelType':'P', + 'ShippingSerialID':'9996999', + 'CarrierPackageID':operation.package_id and operation.package_id.id or False, + }, + 'PhysicalDetails': { + 'PackQualifier':operation.pack_qualifier, + 'PackValue':operation.pack_value, + 'PackSize':operation.pack_size, + 'PackUOM':operation.pack_uom, + 'PackingMedium':operation.packing_medium, + 'PackingMaterial':operation.packing_material, + }, + 'Date': { + 'DateTimeQualifier1':'619', + 'Date1':ship_date, + 'Time1':ship_time, + }, + 'Reference':{ + 'ReferenceQual':operation.reference_qual, + 'ReferenceID':operation.reference_id, + 'Description':operation.ref_description, + }, + 'Notes':{ + 'NoteCode':operation.note_code, + 'NoteInformationField':operation.note_information_field, + }, + } + """packlevel_dict['ItemLevel']['ShipmentLine'] = { + 'LineSequenceNumber':'01', + 'BuyerPartNumber':'9999-SPS', + 'VendorPartNumber':'', + 'ConsumerPackageCode':'FW', + 'EAN':'testReferenceID', + 'GTIN':'New products only. Do not reuse packaging', + 'UPCCaseCode':'FW', + 'NatlDrugCode':'testReferenceID', + 'InternationalStandardBookNumber':'', + 'ProductID':{ + 'PartNumberQual':'STAEV', + 'PartNumber':'REPEAT LOGO PREVIOUS ORDER', + }, + 'OrderQty':'01', + 'OrderQtyUOM':'P', + 'PurchasePrice':'9999-SPS', + 'ItemStatusCode':'', + 'ShipQty':'', + 'ShipQtyUOM':'H1', + 'ProductSizeCode':'S-800', + 'ProductSizeDescription':'Small', + 'ProductColorCode':'C-999', + 'ProductColorDescription':'Fire Truck Red', + 'ProductMaterialDescription':'', + 'NRFStandardColorAndSize':{ + 'NRFColorCode':'600', + 'NRFSizeCode':'42-10651', + }, + 'PhysicalDetails':{ + 'PackQualifier':'Small', + 'PackValue':'C-999', + 'PackSize':'', + 'PackUOM':'', + }, + 'PriceInformation':{ + 'PriceTypeIDCode':'PRP', + 'UnitPrice':'5.48', + }, + 'ProductOrItemDescription':{ + 'ItemDescriptionType':'74', + 'ProductDescription':'Super comfortable', + }, + 'Date': { + 'DateTimeQualifier1':'945', + 'Date1':ship_date, + 'Time1':ship_time, + }, + 'Reference':{ + 'ReferenceQual':'PJ', + 'ReferenceID':'testReferenceID', + 'Description':'New products only. Do not reuse packaging', + }, + 'Notes':{ + 'NoteCode':'DSCSA', + 'NoteInformationField':'REPEAT LOGO PREVIOUS ORDER', + }, + 'ChargesAllowances':{ + 'AllowChrgIndicator':'C', + 'AllowChrgCode':'C310', + 'AllowChrgAmt':'85.02', + 'AllowChrgPercentQual':'4', + 'AllowChrgPercent':'5.0', + 'AllowChrgHandlingCode':'02', + 'AllowChrgHandlingDescription':'This will cover the cost of shipping', + }, + }""" + + shipment_dict.get('Shipment').update(meta_dict) + shipment_dict.get('Shipment').update(header_dict) + shipment_dict.get('Shipment').update(orderlevel_dict) + shipment_dict.get('Shipment').get('OrderLevel').update(packlevel_dict) + + #Sublines + sublines_list = {'Sublines': []} + total_lines = 0 + total_qty = 0 + total_weight = 0 + + #for each sub line + for line in picking.move_lines: + total_lines += 1 + total_qty += line.product_qty + total_weight += (line.product_id.weight * line.product_qty) + subline_dict = {'Subline':{}} + pickingline_dict = {'ShipmentLine': { + 'LineSequenceNumber':int(line.id), + 'BuyerPartNumber':line.product_id.default_code or '', + 'VendorPartNumber':line.product_id.default_code or '', + 'ConsumerPackageCode':line.consumer_package_code, + 'EAN':line.product_id.barcode, + 'GTIN':line.gtin, + 'UPCCaseCode':line.upc_case_code, + 'NatlDrugCode':line.natl_drug_code, + 'InternationalStandardBookNumber':line.international_standard_book_number, + 'ProductID':{ + 'PartNumberQual':'IT', + 'PartNumber':line.product_id.default_code, + }, + 'PurchasePrice':line.price_unit, + 'ShipQty':line.product_qty, + 'ShipQtyUOM':'P4', + 'ProductSizeDescription':line.product_size_description, + 'ProductColorDescription':line.product_color_description, + 'ProductMaterialDescription':line.product_material_description, + } + } + subline_dict.get('Subline').update(pickingline_dict) + sublines_list.get('Sublines').append(subline_dict) + + #shipment_dict.get('Shipment').get('OrderLevel').get('PackLevel').get('ItemLevel').update(sublines_list) + shipment_dict.get('Shipment').get('OrderLevel').update(sublines_list) + + # summary + summary_dict = {'Summary': { + 'TotalLineItems':total_lines or '', + 'TotalQuantity':total_qty, + } + } + shipment_dict.get('Shipment').update(summary_dict) + # update invoice + today = datetime.now().strftime("%Y-%m-%d %H:%M:%S") + picking.write({'856_sent_timestamp':today}) + shipments_list.get('Shipments').append(shipment_dict) + processed += 1 + + #convert dictionary to xml + xml = dicttoxml.dicttoxml(shipments_list, attr_type=False, root=False) + xml = xml.replace('','').replace('','') + xml = xml.replace('','').replace('','') + #Write ASN doc to text file + name = re.findall('\d+', name)[0] + filename = '856_' + today + '%s.xml' % name + filename.replace('/', '_') + fd = open(picking.trading_partner_id.out_path + filename, 'w') + fd.write(xml) + fd.close() + return processed + + def _create_856_wrapper(self, cr, uid, context=None): + + #search for invoices that are edi_yes = True and 856_sent_timestamp = False + eligible_pickings = self.search(cr, uid, [('edi_yes','=',True),('856_sent_timestamp','=',False)], context=context) + return eligible_pickings and self.create_text_856(cr, uid, eligible_pickings, context=context) or False + + # done as a server action + def action_create_text_856(self, cr, uid, ids, context=None): + """ Creates and new 856, ASN and puts it into the outbox + """ + if context is None: + context = {} + + # number of orders to process + toprocess = len(ids) + + # process orders to write 856 + processed = self.create_text_856(cr, uid, ids, context=context) + + return (toprocess - processed) \ No newline at end of file diff --git a/connector_spscommerce/models/stock_move.py b/connector_spscommerce/models/stock_move.py new file mode 100644 index 0000000..2219c41 --- /dev/null +++ b/connector_spscommerce/models/stock_move.py @@ -0,0 +1,61 @@ +# -*- coding: utf-8 -*- +# Copyright (c) Open Source Integrators +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from openerp import api, fields, models + + +class StockMove(models.Model): + _inherit = "stock.move" + + asn_shipment = fields.Char('ASN Shipment Number from 856') + po_number = fields.Char('Line Item PO Number from 856') + edi_line_num = fields.Integer('EDI PO line number') + sale_line_id = fields.Many2one('sale.order.line', 'Sale Order Line', + help='Sale Order Line from Whence this' + ' Stock Move Was created') + so_id = fields.Many2one('sale.order', 'Sale Order', + help='Sale Order from when this Invoice was' + ' created') + edi_yes = fields.Boolean('From an EDI PO?', + help="Is this order from an EDI purchase order," + " 850 EDI doc.") + ship_not_before_date = fields.Date('Do Not Ship Before This Date', + help="Do Not Ship Before This Date.") + cancel_after_date = fields.Date('Cancel if Shipped After This Date', + help="Cancel if Shipped After This Date.") + trading_partner_id = fields.Many2one('edi.config', 'Trading Partner', + help='EDI Configuration information' + ' for partner') + package_code = fields.Selection((('PLT71', 'PLT71'), + ('CTN25', 'CTN25')), + 'Package Code', + help="Pkg Code Qualifier.", + default="PLT71") + + @api.cr_uid_ids_context + def _picking_assign(self, cr, uid, move_ids, context=None): + result = super(StockMove, self).\ + _picking_assign(cr, uid, move_ids, context=context) + pick_ids = {} + + for move in self.browse(cr, uid, move_ids, context=context): + picking = move.picking_id + if not picking or pick_ids.get(picking.id, False): + continue + pick_ids[picking.id] = move.id + edi_yes = move.procurement_id.edi_yes + if edi_yes: + res = {} + res['trading_partner_id'] =\ + move.procurement_id.trading_partner_id and\ + move.procurement_id.trading_partner_id.id or False + res['edi_yes'] = move.procurement_id.trading_partner_id\ + and edi_yes or False + res['ship_not_before_date'] =\ + move.procurement_id.trading_partner_id and\ + move.procurement_id.ship_not_before_date or False + res['cancel_after_date'] =\ + move.procurement_id.trading_partner_id and\ + move.procurement_id.cancel_after_date or False + picking.write(res) + return result diff --git a/connector_spscommerce/readme/CONFIGURE.rst b/connector_spscommerce/readme/CONFIGURE.rst new file mode 100644 index 0000000..a2546bb --- /dev/null +++ b/connector_spscommerce/readme/CONFIGURE.rst @@ -0,0 +1,4 @@ + +To configure this module, you need to add a cron job: + +`*/15 * * * * python /opt/local-addons/connector_spscommerce/edi_scripts/edi_process_in.py` \ No newline at end of file diff --git a/connector_spscommerce/readme/CONTRIBUTORS.rst b/connector_spscommerce/readme/CONTRIBUTORS.rst new file mode 100644 index 0000000..09efd92 --- /dev/null +++ b/connector_spscommerce/readme/CONTRIBUTORS.rst @@ -0,0 +1,5 @@ + +* Adam O'Connor +* Sandip Mangukiya +* Maxime Chambreuil + diff --git a/connector_spscommerce/readme/CREDITS.rst b/connector_spscommerce/readme/CREDITS.rst new file mode 100644 index 0000000..6fd11e3 --- /dev/null +++ b/connector_spscommerce/readme/CREDITS.rst @@ -0,0 +1,3 @@ + +* Open Source Integrators + diff --git a/connector_spscommerce/readme/DESCRIPTION.rst b/connector_spscommerce/readme/DESCRIPTION.rst new file mode 100644 index 0000000..cc848bf --- /dev/null +++ b/connector_spscommerce/readme/DESCRIPTION.rst @@ -0,0 +1,11 @@ +This module provides EDI integration using SPS Commerce (https://www.spscommerce.com). +It adds a new tree view to the Partner record for EDI settings. +EDI documents will interface with this module to pull its settings. +It adds a few flags to interact with EDI conversion scripts to import and export EDI documents. + +List of supported EDI documents: + +* 810 Customer Invoice - Export +* 850 Customer PO - Import +* 855 Order Acknowledgement - Export +* 856 Advance Shipment Notice - Export diff --git a/connector_spscommerce/readme/INSTALL.rst b/connector_spscommerce/readme/INSTALL.rst new file mode 100644 index 0000000..247501e --- /dev/null +++ b/connector_spscommerce/readme/INSTALL.rst @@ -0,0 +1,4 @@ + +To install this module, you need to: + +`$ pip install XX` \ No newline at end of file diff --git a/connector_spscommerce/readme/USAGE.rst b/connector_spscommerce/readme/USAGE.rst new file mode 100644 index 0000000..e69de29 diff --git a/connector_spscommerce/static/src/img/icon.png b/connector_spscommerce/static/src/img/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..1622a175d9c8f0458aabcacd23252356e84ddaa3 GIT binary patch literal 4235 zcmV;65OnW}P)YH@3OtM%h@TebE| zo4Qq{wO)&U+)=KjIuodVY^ho>kf0UxTCWOfgBKMtldwgUEoA0A_m4?oGIM6lIU(5d z`+V|8-ZSSt&pE&MS>ES;-v?Z{aN)v*3l}b2xNzaZg$oxhT)1%Iq5w#rmvBjQDZ9#! zMLlCc$AFAMr5s@=y6s{D6$FNQKtqIT5D0g3YeU5r4v|&YCTbT zj#<7ft`BisBO?EX=xx9W39VZ|@26Ad{WFnpU) z00TH4)uS-=Z~zRAVoDfd>j;ck_r)ZnJ`zCnabUnctw3%;%kLl{v0i)A#jQtTnv+qP z0!#rNpP;E$*aj>I7PH%Uwzo{FvxJ0NZUDJ6;W_OX8la|PspD_9XtNh{?-WF;P^JK5 zd+@&PAdk?gbDa)0oFgO@z6rP^q2*&#&md6ofinbb9F7`OQKleNq5O*jPuUJ~D_VZ9 zQ`+qeA@$+wQ0_^1%HQeKuXAC=2K)R{>nIGZ0&^-VRlw+8H1#VnJQq_l?Em^cA)&}@ z&?gf5ZYCO?%7sUT3+_`@_(R)xqOqxnd=23=lo1CwAZkbD0s`I@eV346%ejawVtv5n>Ol2k0u|5nJwl!ikHy35-J>b%jy0l&86%Za2CQWpc**BCdoR0)d-C! zjcC!UInQ~5t)me0Cg5VABrUTwF$)ZMNnU!PuvWwL|K8MjhMc-dv$^tY`Gkfhk?|!80AI$-ZK%= z14Bq~O$jpSU7&J*k9s+^6_01J&Br6=%fOHvp3(-=fYOLTBQ@n~d-B3IWgmWs>OAW& zn#21$Mo4@nepkW>c?G|Ba>fr9j10xl)hI`2NLj1^Z9t5dsr9a*FCrBF4REcc25am% z0wcB+(rw{OxWP^35WTrun@8qk9lgS%X~&8%T<`F2^@ zCV}TRAA-vFy7wfvWT#)|SPY4e(Dv&OIx+wP-n9t7wYbuslIV{L>9R0FIyx@{4o~>1 z>zGl#Jo9J@PXs1eTzToB-pb?YxE1&;)uhSg%wJ#TybnRYdZZUcHj4kLVub$?8t zP7UhBB&Z%}od`M&;a^dXMkI>pJD9T2?!1vLxM0+$gdz`u-jH-~q2opj?sWYBNd%;RN8&481%^t@p~b%56E;h(1d^_1#k(26|bF9Qol!g5jfV<)dit3zqLEoaJ1URm5qj10%XBr+aHh zG^28BA92Mc2(xW>?(kQORLUvN5u)8Tw*eB)D@73S-h%M`cz!Ac?I_QJUX7urBMD2u z&R|l(8H!NLOzOiA5eiRE`X0+HzF8$pnoFJWA;L-B?X~8gXR7r*LY+Rw@sSgV>2M4b zq#4uOoRqa)N+^5=Fg}M~H^*~d(g4zgS-KH(j}H$&1ujZ6xQ66RfOJkpq&mKO89f#x z?zfbOyZ<=0(qfDk@!5dUzyWwIVOpq^#zTxc&_TruD|pF#G=T6EDrlQ)R=>R z*t-@{({@y6Qy&=)OtlzX*88j$hKEqX6jZB0sv$aV9|7I52V!fx%iRABWRIpquZ9k{ zpAgTck(epVn429lDD~lQfgGLXSAQf>{!WHejS_5GgveBjs~_)619j}J?$Us2gyT_+ z9A*ST0wZ>JH_3yy6AEttx(5t~G01+CWp<8AQAg7uLh8fkqdby>4b8sYQ_`#+igXYP zhuGVBHX>zt&dS<}>dk4cZA>8q`q@v2c#c3dS?%}OLO`7wObCA&HKw6@L2B@j&a6e7 zbNJQ7G$!$?0;S!In&)xh@N|33ECLpFfDls;PZ>c<;WNG9hB1gIer%0MH7Y|u^BJYP z@DC4XdB$MiTQ=$K4ut>4FzT4$+ms>jX|?z&PH2c6lH%D6DbAgM$WTPT0WuwQx`kS& z54J67l6Bb^!#ww8$&F<~2g)i$8&IQxfjeH~tjdlYvKgCF#5Hj!>`hghBXc6cFz-fn z7EnTdO0^Z$Iz%ki9CTfl6wmFzxAS;b6y$Z#1`KVWL|)|_->w33I~Lgp-C;i=s+u}Z zr9889j*Lfj87jjHV0#5oxr8DaYfWMrhyK*oL#Yox0uspK8J{5f3~Dr>o+f(^4T)G- zl24t`kW^tc10lgp1JUT8fPBa>L6;-kyKg68efV5!rPBQlJ-++pWYBY=;}Vb|IefQy z98rE9)nZ3}EiqM6nkuhsbAS-pmcqR1@C=%Jb&d(Z2(S$FWeoibHN!V1kv_&!LzF`a zg@4*Lcla96KaYsu&(xG(?a;Du>Kbo(b{rUfS7hyi(vXwoys)f0WOjfjmssuumYe zJ0+%8PD>%=vn~A)=Ap8b-O^a_`Mig;7$Qd(aAyRS2NC^68do)sVB~HLhJa4)T5TL1 z53svbuAkXKDDqzn*zvQR!rlD?@wlXsyec2Tb0;JqF+$-rq@=B7IwWm{IdACaz>j)7 zl8@MUH0sGG@c01qamr*KJ9Q);ev!u9Re@GzQM^*n8`wFZ64jcVOx)JU81~g=+4roK zd0Er^A_!iwXq`rIO^GuZ#-hqBKiCcWGB5{&iTHiT;jg%xb9}onirQ@w;sO>$X5~oFdvKa z?kirm!*~ z;Uh{lgi|o&T#MEYqMqMnystxN(ml<;o#Nt03lgCq!ApzpCw|f?c_iIPuyvG8IfzON zJN|>hQsTL2WaJW5troN9XFTW8phc*0Q&KSUaMxU{mk2p;=;t8!T3qRem`}!cvG+98 z?B>vSpmHI#z8CE!K|R!mClQJ~j;PhSz)fiEKQm7TTfg14se50Z0<*Z_J!OS2P-*m6 zi09JO+zpeFjK_y_4K*u)T|fxKbBir?MH;k-t!)!AwF=QG5IGIyur$F{4ERpQjerkn zG1{86*+FmbngbR1+C4$2^=vSow{(xH-y=}*SiyZSY8y>#Z=^>-LU2t9p2JVXlqo1x zU1bFa<}?LHuEOtqAoKf2=71)i9BW3~C-me1QqRw+hr&+~FFlbOyNi66TJMwe7M>6H zXSZ<*qLaa_0yzcfpJy*o;0pY{N3-~xafrSd&w1?&kIyFH{Zr2jLr<}wbpZX$)xbDw zO_4g*mfz;c$=av39!lBX)4DRYRltd4E*6vRYiz(|76ISNtiFfvN+dCNr4Qh*_+ihJ z0(PK2d>qP3(ie+lU(NV0iGvj=_{; zKu$)q3gs9ZrE3G4;zepa%UL^YP2OfS7@mmG#QsIPPDIY9*1N>E?49j)J#N01ScJp9 zjTVFzD2>EuWTx-Eo>_nmV-Qah@Q zuSS{6{_=i%L4Sb1V!l(4?A>Dg_2Ee_qJc0Ce3_nHr8={S1xjt!Im$srNFtUrm$GZ%7*q#?4n~Yl#N19yHW2WAa=_#YMrslLHQjgT zFQ(ITWg*pl4jw}K8iFmyB62VA&4ku=l%EjrJ=|MjxP;WVd< literal 0 HcmV?d00001 diff --git a/connector_spscommerce/views/account_invoice_view.xml b/connector_spscommerce/views/account_invoice_view.xml new file mode 100644 index 0000000..ed12086 --- /dev/null +++ b/connector_spscommerce/views/account_invoice_view.xml @@ -0,0 +1,75 @@ + + + + + + + invoice.edi.view2 + account.invoice + form + + + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + Create 810 + + code + self.action_create_text_810(cr, uid, context.get('active_ids', []), context=context) + True + + + + + + + Create 810 + + + + +
+
diff --git a/connector_spscommerce/views/company_config_settings_view.xml b/connector_spscommerce/views/company_config_settings_view.xml new file mode 100644 index 0000000..39d35ce --- /dev/null +++ b/connector_spscommerce/views/company_config_settings_view.xml @@ -0,0 +1,132 @@ + + + + + + qb.edi.tree.view + edi.config + tree + + + + + + + + + + + + + + + + + + + + + + + qb.edi.form + edi.config + form + +
+ + + + + + + + + + + + + + + + +
+
+
+ + + + EDI Configuration + ir.actions.act_window + edi.config + form + tree,form + + + + + + form + + + + + + + + tree + + + + + + + + company.inherit.form + res.company + + + + + + + + + + + partner.inherit.form + res.partner + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
diff --git a/connector_spscommerce/views/sale_view.xml b/connector_spscommerce/views/sale_view.xml new file mode 100644 index 0000000..c11e0ce --- /dev/null +++ b/connector_spscommerce/views/sale_view.xml @@ -0,0 +1,105 @@ + + + + + + + purchase.line.edi.view + purchase.order.line + form + + + + + + + + + + + + so.edi.view + sale.order + form + + + + + + + + +
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
diff --git a/connector_spscommerce/views/stock_view.xml b/connector_spscommerce/views/stock_view.xml new file mode 100644 index 0000000..bc7b8b0 --- /dev/null +++ b/connector_spscommerce/views/stock_view.xml @@ -0,0 +1,147 @@ + + + + + + + picking.out.landed.form + stock.move + + + + + + + + + + + picking.out.landed.tree + stock.move + + + + + + + + + + + stock.transfer.pack.view + stock.pack.operation + + + + + + + + + + stock.quant.pack.view2 + stock.quant.package + + + + + + + + + + + del.carrier.view + delivery.carrier + + + + + + + + + + + + stock.move.edi.view + stock.move + form + + + + + + + + + + + do.edi.view + stock.picking + form + + + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Create 856 + + code + self.action_create_text_856(cr, uid, context.get('active_ids', []), context=context) + True + + + + + + + Create 856 + + + + +
+
From 1b8c4322200690971e3da9c02d85660cda530751 Mon Sep 17 00:00:00 2001 From: Sandip Mangukiya Date: Mon, 20 Aug 2018 17:31:28 +0530 Subject: [PATCH 2/5] Code Formatting --- .../models/account_invoice.py | 28 +- connector_spscommerce/models/edi_config.py | 6 +- connector_spscommerce/models/sale.py | 366 +++++++++--------- connector_spscommerce/models/stock.py | 56 +-- connector_spscommerce/models/stock_move.py | 6 +- 5 files changed, 231 insertions(+), 231 deletions(-) diff --git a/connector_spscommerce/models/account_invoice.py b/connector_spscommerce/models/account_invoice.py index c9ccad3..c8f1c17 100644 --- a/connector_spscommerce/models/account_invoice.py +++ b/connector_spscommerce/models/account_invoice.py @@ -76,7 +76,7 @@ class account_invoice(osv.osv): 'ship_not_before_date': fields.date('Estimated Shipping Date', help="This is the date from the 856."), 'cancel_after_date': fields.date('Cancel if Shipped After This Date', help="Cancel if Shipped After This Date."), 'sale_id': fields.many2one('sale.order', 'Sale Order', help='Sale Order from Whence this Invoice Was created'), - 'picking_id': fields.many2one('stock.picking','Picking ID', help='Stock Picking ID whence this invoice was created'), + 'picking_id': fields.many2one('stock.picking', 'Picking ID', help='Stock Picking ID whence this invoice was created'), 'scac_code': fields.char('SCAC Code', help="This is the shipping alpha code from your carrier."), 'bol_num': fields.char('BoL Number', help="This is bill of lading number from your carrier/shipper."), 'tracking_num': fields.char('Supplier Code', help="This is the tracking number from your carrier."), @@ -132,29 +132,29 @@ def create_text_810(self, cr, uid, invoice_ids, context=None): invoices_list = {'Invoices':[]} for invoice in self.browse(cr, uid, invoice_ids, context=context): num += invoice.number - #skip non customer invoice records - if invoice.type not in ('out_invoice') or invoice.state in ('draft','cancel','paid'): + # skip non customer invoice records + if invoice.type not in ('out_invoice') or invoice.state in ('draft', 'cancel', 'paid'): continue - #Grab the vendor_id and trading_partner_id for EDI in the edi_config. + # Grab the vendor_id and trading_partner_id for EDI in the edi_config. trading_partner_code = invoice.trading_partner_id.partner_header_string vendor_code = invoice.trading_partner_id.vendor_header_string date_format = "%Y-%m-%d" - #invoice date + # invoice date inv_date = invoice.date_invoice dateinv_object = datetime.strptime(inv_date, date_format) inv_date = dateinv_object.date() inv_date = str(inv_date) - #purchase date + # purchase date po_date = invoice.purchase_id and invoice.purchase_id.date_order or '' if po_date: datepo_object = datetime.strptime(po_date, date_format) po_date = datepo_object.date() po_date = str(po_date) - #ship date + # ship date ship_date = invoice.picking_id and invoice.picking_id.date_done or '' if ship_date: ship_object = datetime.strptime(ship_date, date_format) @@ -271,12 +271,12 @@ def create_text_810(self, cr, uid, invoice_ids, context=None): } invoice_dict.get('Invoice').update(meta_dict) invoice_dict.get('Invoice').update(header_dict) - #LineItems + # LineItems lineitems_list = {'LineItems': []} total_lines = 0 total_qty = 0 total_weight = 0 - #for each item line + # for each item line for line in invoice.invoice_line_ids: total_lines += 1 total_qty += line.quantity @@ -306,7 +306,7 @@ def create_text_810(self, cr, uid, invoice_ids, context=None): 'ProductMaterialDescription':line.product_material_description or '', 'NRFStandardColorAndSize':{ 'NRFColorCode':'600', - 'NRFSizeCode':'42-10651', + 'NRFSizeCode':'42-10651', } }, 'ProductOrItemDescription':{ @@ -366,8 +366,8 @@ def create_text_810(self, cr, uid, invoice_ids, context=None): processed += 1 # Convert dictionary to xml xml = dicttoxml.dicttoxml(invoices_list, attr_type=False, root=False) - xml = xml.replace('','').replace('','') - xml = xml.replace('','').replace('','') + xml = xml.replace('', '').replace('', '') + xml = xml.replace('', '').replace('', '') # Write ASN doc to text file num = re.findall('\d+', num)[0] filename = '810_' + today + '%s.xml' % num @@ -378,8 +378,8 @@ def create_text_810(self, cr, uid, invoice_ids, context=None): return processed def _create_810_wrapper(self, cr, uid, context=None): - #search for invoices that are edi_yes = True and 810_sent_timestamp = False. - eligible_invoices = self.search(cr, uid, [('edi_yes','=',True),('810_sent_timestamp','=',False)], context=context) + # search for invoices that are edi_yes = True and 810_sent_timestamp = False. + eligible_invoices = self.search(cr, uid, [('edi_yes', '=', True), ('810_sent_timestamp', '=', False)], context=context) return eligible_invoices and self.create_text_810(cr, uid, eligible_invoices, context=context) or False # done as a server action diff --git a/connector_spscommerce/models/edi_config.py b/connector_spscommerce/models/edi_config.py index 2915e16..e67d6bb 100644 --- a/connector_spscommerce/models/edi_config.py +++ b/connector_spscommerce/models/edi_config.py @@ -10,11 +10,11 @@ class edi_config(osv.osv): _columns = { 'edi_company_id': fields.many2one('res.company', - string ='EDI Company', + string='EDI Company', help="The Main EDI Company.", required=False, ondelete='cascade'), 'route_id': fields.many2one('stock.location.route', - string ='Stock Transfer Route', + string='Stock Transfer Route', help="""Select Dropshipping if applicable or another route. This will be the route used in sale order lines."""), @@ -78,4 +78,4 @@ class res_partner(orm.Model): 'EDI Company ID'), 'ship_to_code': fields.char(string='Ship to Code',), 'sender_id': fields.char(string='EDI Sender Code',), - } \ No newline at end of file + } diff --git a/connector_spscommerce/models/sale.py b/connector_spscommerce/models/sale.py index aa32045..43967b9 100644 --- a/connector_spscommerce/models/sale.py +++ b/connector_spscommerce/models/sale.py @@ -76,8 +76,8 @@ def create_855(self, cr, uid, sale_ids, context=None): for sale_id in sale_ids: # initialize - csvwriter=None - sale_obj = self.browse(cr,uid, sale_id, context=context) + csvwriter = None + sale_obj = self.browse(cr, uid, sale_id, context=context) config_obj = sale_obj.trading_partner_id OUT_PATH = config_obj.out_path sale_lines = sale_obj.order_line @@ -88,19 +88,19 @@ def create_855(self, cr, uid, sale_ids, context=None): if sale_obj.client_order_ref: po_num = sale_obj.client_order_ref - #create and format dates for ACK + # create and format dates for ACK now = datetime.now() today = now.strftime('%Y%m%d') time = datetime.now().strftime('%H%M%S') - po_date = datetime.strptime(sale_obj.date_order,'%Y-%m-%d %H:%M:%S') + po_date = datetime.strptime(sale_obj.date_order, '%Y-%m-%d %H:%M:%S') po_date = po_date.strftime('%m/%d/%Y') - #generate random numbers for the 3 header id numbers in 855 - ISA_num = randint(100000000,999999999) - GS_code = randint(1000000,9999999) - st_trans_num = randint(100000000,999999999) + # generate random numbers for the 3 header id numbers in 855 + ISA_num = randint(100000000, 999999999) + GS_code = randint(1000000, 9999999) + st_trans_num = randint(100000000, 999999999) - #Grab the vendor_id and trading_partner_id for EDI in the edi_config. + # Grab the vendor_id and trading_partner_id for EDI in the edi_config. trading_partner_code = sale_obj.trading_partner_id.partner_header_string trading_partner_padded = trading_partner_code.ljust(15) @@ -108,98 +108,98 @@ def create_855(self, cr, uid, sale_ids, context=None): vendor_padded = vendor_code.ljust(15) OUT_PATH = str(sale_obj.trading_partner_id.out_path) - #header, payment and date info - xml_output += '' +trading_partner_code+ '' - xml_output += '' +po_num+ '' - xml_output += ''+str(sale_obj.tset_purpose_code)+'' - xml_output += '' +po_date+ '' - xml_output += '' +str(sale_obj.purchase_order_type_code)+ '' - xml_output += '' +str(sale_obj.po_type_description)+ '' - xml_output += '' +po_num+ '' + # header, payment and date info + xml_output += '' + trading_partner_code + '' + xml_output += '' + po_num + '' + xml_output += '' + str(sale_obj.tset_purpose_code) + '' + xml_output += '' + po_date + '' + xml_output += '' + str(sale_obj.purchase_order_type_code) + '' + xml_output += '' + str(sale_obj.po_type_description) + '' + xml_output += '' + po_num + '' xml_output += 'RJ' - xml_output += '' +today+ '' - xml_output += '' +str(sale_obj.ship_complete_code)+ '' - xml_output += ''+str(sale_obj.pricelist_id.currency_id.name)+'' - xml_output += '' +str(sale_obj.department)+ '' - xml_output += '' +str(sale_obj.division)+ '' - xml_output += '' +str(sale_obj.name)+ '' - xml_output += '' +str(sale_obj.promotion_deal_number)+ '' - xml_output += '' +vendor_code+ '' - xml_output += ''+str(sale_obj.terms_type)+'' - xml_output += '' +str(sale_obj.terms_basis_date_code)+ '' - xml_output += '' +str(sale_obj.terms_discount_percentage)+ '' - xml_output += '' +str(sale_obj.terms_discount_due_days)+ '' - xml_output += '' +str(sale_obj.terms_net_due_days)+ '' - xml_output += '' +str(sale_obj.payment_term_id.note)+ '' - xml_output += '' +str(sale_obj.payment_method_code)+ '' + xml_output += '' + today + '' + xml_output += '' + str(sale_obj.ship_complete_code) + '' + xml_output += '' + str(sale_obj.pricelist_id.currency_id.name) + '' + xml_output += '' + str(sale_obj.department) + '' + xml_output += '' + str(sale_obj.division) + '' + xml_output += '' + str(sale_obj.name) + '' + xml_output += '' + str(sale_obj.promotion_deal_number) + '' + xml_output += '' + vendor_code + '' + xml_output += '' + str(sale_obj.terms_type) + '' + xml_output += '' + str(sale_obj.terms_basis_date_code) + '' + xml_output += '' + str(sale_obj.terms_discount_percentage) + '' + xml_output += '' + str(sale_obj.terms_discount_due_days) + '' + xml_output += '' + str(sale_obj.terms_net_due_days) + '' + xml_output += '' + str(sale_obj.payment_term_id.note) + '' + xml_output += '' + str(sale_obj.payment_method_code) + '' xml_output += 'ORS' - xml_output += '' +today+ '' + xml_output += '' + today + '' cust_rec = sale_obj.partner_id ship_address_rec = sale_obj.partner_shipping_id - #customer contact details + # customer contact details xml_output += 'CH' - xml_output += '' +str(cust_rec.name)+ '' - xml_output += '' +str(cust_rec.phone)+ '' - xml_output += '' +str(cust_rec.fax)+ '' - xml_output += '' +str(cust_rec.email)+ '' + xml_output += '' + str(cust_rec.name) + '' + xml_output += '' + str(cust_rec.phone) + '' + xml_output += '' + str(cust_rec.fax) + '' + xml_output += '' + str(cust_rec.email) + '' - #contact address details + # contact address details xml_output += '
FW' xml_output += '1' xml_output += '11111' - xml_output += '' +str(ship_address_rec.name)+ '' - xml_output += '' +str(ship_address_rec.street)+ '' - xml_output += '' +str(ship_address_rec.street2)+ '' - xml_output += '' +str(ship_address_rec.city)+ '' - xml_output += '' +str(ship_address_rec.state_id.code)+ '' - xml_output += '' +str(ship_address_rec.zip)+ '' - xml_output += '' +str(ship_address_rec.country_id.code)+ '' + xml_output += '' + str(ship_address_rec.name) + '' + xml_output += '' + str(ship_address_rec.street) + '' + xml_output += '' + str(ship_address_rec.street2) + '' + xml_output += '' + str(ship_address_rec.city) + '' + xml_output += '' + str(ship_address_rec.state_id.code) + '' + xml_output += '' + str(ship_address_rec.zip) + '' + xml_output += '' + str(ship_address_rec.country_id.code) + '' xml_output += 'CH' - xml_output += '' +str(cust_rec.name)+ '' - xml_output += '' +str(cust_rec.phone)+ '' - xml_output += '' +str(cust_rec.fax)+ '' - xml_output += '' +str(cust_rec.email)+ '
' + xml_output += '' + str(cust_rec.name) + '' + xml_output += '' + str(cust_rec.phone) + '' + xml_output += '' + str(cust_rec.fax) + '' + xml_output += '' + str(cust_rec.email) + '' - #FOBRelatedInstruction - xml_output += ''+str(sale_obj.fob_pay_code)+'' - xml_output += '' +str(sale_obj.fob_location_qualifier)+ '' - xml_output += '' +str(sale_obj.fob_location_description)+ '' - xml_output += '' +str(sale_obj.fob_title_passage_code)+ '' - xml_output += '' +str(sale_obj.fob_title_passage_location)+ '' + # FOBRelatedInstruction + xml_output += '' + str(sale_obj.fob_pay_code) + '' + xml_output += '' + str(sale_obj.fob_location_qualifier) + '' + xml_output += '' + str(sale_obj.fob_location_description) + '' + xml_output += '' + str(sale_obj.fob_title_passage_code) + '' + xml_output += '' + str(sale_obj.fob_title_passage_location) + '' - #CarrierInformation - xml_output += ''+str(sale_obj.carrier_trans_method_code)+'' - xml_output += '' +str(sale_obj.carrier_alpha_code)+ '' - xml_output += '' +str(sale_obj.carrier_routing)+ '' - xml_output += '' +str(sale_obj.routing_sequence_code)+ '' - xml_output += '' +str(sale_obj.service_leve_code)+ '' + # CarrierInformation + xml_output += '' + str(sale_obj.carrier_trans_method_code) + '' + xml_output += '' + str(sale_obj.carrier_alpha_code) + '' + xml_output += '' + str(sale_obj.carrier_routing) + '' + xml_output += '' + str(sale_obj.routing_sequence_code) + '' + xml_output += '' + str(sale_obj.service_leve_code) + '' - #Reference - xml_output += ''+str(sale_obj.reference_qual)+'' - xml_output += '' +str(sale_obj.reference_id)+ '' - xml_output += '' +str(sale_obj.ref_description)+ '' + # Reference + xml_output += '' + str(sale_obj.reference_qual) + '' + xml_output += '' + str(sale_obj.reference_id) + '' + xml_output += '' + str(sale_obj.ref_description) + '' - #Notes - xml_output += ''+str(sale_obj.note_code)+'' - xml_output += '' +str(sale_obj.note_information_field)+ '' + # Notes + xml_output += '' + str(sale_obj.note_code) + '' + xml_output += '' + str(sale_obj.note_information_field) + '' - #ChargesAllowances - xml_output += ''+str(sale_obj.allow_chrg_indicator)+'' - xml_output += '' +str(sale_obj.allow_chrg_code)+ '' - xml_output += '' +str(sale_obj.allow_chrg_agency_code)+ '' - xml_output += '' +str(sale_obj.allow_chrg_agency)+ '' - xml_output += '' +str(sale_obj.allow_chrg_amt)+ '' - xml_output += '' +str(sale_obj.allow_chrg_percent_qual)+ '' - xml_output += '' +str(sale_obj.allow_chrg_percent)+ '' - xml_output += '' +str(sale_obj.allow_chrg_handling_code)+ '' - xml_output += '' +str(sale_obj.reference_identification)+ '' - xml_output += '' +str(sale_obj.allow_chrg_handling_description)+ '' + # ChargesAllowances + xml_output += '' + str(sale_obj.allow_chrg_indicator) + '' + xml_output += '' + str(sale_obj.allow_chrg_code) + '' + xml_output += '' + str(sale_obj.allow_chrg_agency_code) + '' + xml_output += '' + str(sale_obj.allow_chrg_agency) + '' + xml_output += '' + str(sale_obj.allow_chrg_amt) + '' + xml_output += '' + str(sale_obj.allow_chrg_percent_qual) + '' + xml_output += '' + str(sale_obj.allow_chrg_percent) + '' + xml_output += '' + str(sale_obj.allow_chrg_handling_code) + '' + xml_output += '' + str(sale_obj.reference_identification) + '' + xml_output += '' + str(sale_obj.allow_chrg_handling_description) + '' - total_lines=0 - total_qty=0 - total_weight=0 - #Create the text string for 855 order acknowledgement + total_lines = 0 + total_qty = 0 + total_weight = 0 + # Create the text string for 855 order acknowledgement for line in sale_lines: total_lines += 1 est_del_date = '' @@ -207,21 +207,21 @@ def create_855(self, cr, uid, sale_ids, context=None): accept_code = '' if line.edi_est_ship_date: - est_ship_date = datetime.strptime(line.edi_est_ship_date,'%Y-%m-%d') + est_ship_date = datetime.strptime(line.edi_est_ship_date, '%Y-%m-%d') est_ship_date = est_ship_date.strftime('%m/%d/%Y') if line.edi_line_msg == 'reject': - accept_code='CC' + accept_code = 'CC' elif line.edi_line_msg == 'backorder': - accept_code='IB' + accept_code = 'IB' else: - accept_code='IA' + accept_code = 'IA' uom = line.product_uom.name quantity = int(line.product_uom_qty) - total_qty+=quantity + total_qty += quantity total_weight += (line.product_id.weight * quantity) original_po_qty = int(line.edi_line_qty) @@ -231,115 +231,115 @@ def create_855(self, cr, uid, sale_ids, context=None): else: print "***** UOM is Not EA (Each) *****" - #line items + # line items xml_output += '' - xml_output += '' +str(line.id)+ '' - xml_output += '' +str(line.buyer_part_number)+ '' - xml_output += '' +str(line.vendor_part_number)+ '' - xml_output += '' +str(line.consumer_package_code)+ '' - xml_output += '' +str(line.gtin)+ '' - xml_output += '' +str(line.upc_case_code)+ '' + xml_output += '' + str(line.id) + '' + xml_output += '' + str(line.buyer_part_number) + '' + xml_output += '' + str(line.vendor_part_number) + '' + xml_output += '' + str(line.consumer_package_code) + '' + xml_output += '' + str(line.gtin) + '' + xml_output += '' + str(line.upc_case_code) + '' xml_output += 'MN' - xml_output += '' +str(line.product_id.default_code)+ '' - xml_output += '' +str(uom)+ '' - xml_output += '' +str(line.edi_line_qty)+ '' - xml_output += '' +str(line.price_unit)+ '' - xml_output += '' +str(line.purchase_price_basis)+ '' - xml_output += '' +str(sale_obj.pricelist_id.currency_id.name)+ '' - xml_output += '' +str(line.product_size_code)+ '' - xml_output += '' +str(line.product_size_description)+ '' - xml_output += '' +str(line.product_color_code)+ '' - xml_output += '' +str(line.product_color_description)+ '' - xml_output += '' +str(line.product_material_code)+ '' - xml_output += '' +str(line.product_material_description)+ '' - xml_output += '' +str(line.department)+ '' - xml_output += '' +str(line.classs)+ '' + xml_output += '' + str(line.product_id.default_code) + '' + xml_output += '' + str(uom) + '' + xml_output += '' + str(line.edi_line_qty) + '' + xml_output += '' + str(line.price_unit) + '' + xml_output += '' + str(line.purchase_price_basis) + '' + xml_output += '' + str(sale_obj.pricelist_id.currency_id.name) + '' + xml_output += '' + str(line.product_size_code) + '' + xml_output += '' + str(line.product_size_description) + '' + xml_output += '' + str(line.product_color_code) + '' + xml_output += '' + str(line.product_color_description) + '' + xml_output += '' + str(line.product_material_code) + '' + xml_output += '' + str(line.product_material_description) + '' + xml_output += '' + str(line.department) + '' + xml_output += '' + str(line.classs) + '' - #date - xml_output += '' +str(line.price_unit)+ '' - xml_output += '' +today+ '' + # date + xml_output += '' + str(line.price_unit) + '' + xml_output += '' + today + '' - #PriceInformation - xml_output += '' +str(line.price_unit)+ '' - xml_output += '' +str(line.price_unit)+ '' - xml_output += '' +str(quantity)+ '' - xml_output += '' +str(line.multiple_price_quantity)+ '' - xml_output += '' +str(line.class_of_trade_code)+ '' + # PriceInformation + xml_output += '' + str(line.price_unit) + '' + xml_output += '' + str(line.price_unit) + '' + xml_output += '' + str(quantity) + '' + xml_output += '' + str(line.multiple_price_quantity) + '' + xml_output += '' + str(line.class_of_trade_code) + '' - #ProductOrItemDescription - xml_output += '' +str(line.item_description_type)+ '' - xml_output += '' +str(line.product_characteristic_code)+ '' - xml_output += '' +str(line.agency_qualifier_code)+ '' - xml_output += '' +str(line.product_description_code)+ '' - xml_output += '' +str(line.name)+ '' + # ProductOrItemDescription + xml_output += '' + str(line.item_description_type) + '' + xml_output += '' + str(line.product_characteristic_code) + '' + xml_output += '' + str(line.agency_qualifier_code) + '' + xml_output += '' + str(line.product_description_code) + '' + xml_output += '' + str(line.name) + '' - #PhysicalDetails - xml_output += '' +str(line.pack_qualifier)+ '' - xml_output += '' +str(line.pack_value)+ '' - xml_output += '' +str(line.pack_size)+ '' - xml_output += '' +str(line.pack_uom)+ '' - xml_output += '' +str(line.packing_medium)+ '' - xml_output += '' +str(line.packing_material)+ '' - xml_output += '' +str(line.pack_weight)+ '' - xml_output += '' +str(line.pack_weight_uom)+ '' + # PhysicalDetails + xml_output += '' + str(line.pack_qualifier) + '' + xml_output += '' + str(line.pack_value) + '' + xml_output += '' + str(line.pack_size) + '' + xml_output += '' + str(line.pack_uom) + '' + xml_output += '' + str(line.packing_medium) + '' + xml_output += '' + str(line.packing_material) + '' + xml_output += '' + str(line.pack_weight) + '' + xml_output += '' + str(line.pack_weight_uom) + '' - #Reference - xml_output += ''+str(sale_obj.reference_qual)+'' - xml_output += '' +str(sale_obj.reference_id)+ '' - xml_output += '' +str(sale_obj.ref_description)+ '' + # Reference + xml_output += '' + str(sale_obj.reference_qual) + '' + xml_output += '' + str(sale_obj.reference_id) + '' + xml_output += '' + str(sale_obj.ref_description) + '' - #Notes - xml_output += ''+str(sale_obj.note_code)+'' - xml_output += '' +str(sale_obj.note_information_field)+ '' + # Notes + xml_output += '' + str(sale_obj.note_code) + '' + xml_output += '' + str(sale_obj.note_information_field) + '' - #contact address details + # contact address details xml_output += '
FW' - xml_output += '' +str(ship_address_rec.name)+ '' - xml_output += '' +str(ship_address_rec.street)+ '' - xml_output += '' +str(ship_address_rec.city)+ '' - xml_output += '' +str(ship_address_rec.state_id.code)+ '' - xml_output += '' +str(ship_address_rec.zip)+ '' - xml_output += '' +str(ship_address_rec.country_id.code)+ '
' + xml_output += '' + str(ship_address_rec.name) + '' + xml_output += '' + str(ship_address_rec.street) + '' + xml_output += '' + str(ship_address_rec.city) + '' + xml_output += '' + str(ship_address_rec.state_id.code) + '' + xml_output += '' + str(ship_address_rec.zip) + '' + xml_output += '' + str(ship_address_rec.country_id.code) + '' - #ChargesAllowances - xml_output += ''+str(line.allow_chrg_indicator)+'' - xml_output += '' +str(line.allow_chrg_code)+ '' - xml_output += '' +str(line.allow_chrg_agency_code)+ '' - xml_output += '' +str(line.allow_chrg_agency)+ '' - xml_output += '' +str(line.allow_chrg_amt)+ '' - xml_output += '' +str(line.allow_chrg_percent)+ '' - xml_output += '' +str(line.percent_dollar_basis)+ '' - xml_output += '' +str(line.allow_chrg_rate)+ '' - xml_output += '' +str(line.allow_chrg_qty_uom)+ '' - xml_output += '' +str(line.allow_chrg_handling_code)+ '' - xml_output += '' +str(line.allow_chrg_handling_description)+ '' + # ChargesAllowances + xml_output += '' + str(line.allow_chrg_indicator) + '' + xml_output += '' + str(line.allow_chrg_code) + '' + xml_output += '' + str(line.allow_chrg_agency_code) + '' + xml_output += '' + str(line.allow_chrg_agency) + '' + xml_output += '' + str(line.allow_chrg_amt) + '' + xml_output += '' + str(line.allow_chrg_percent) + '' + xml_output += '' + str(line.percent_dollar_basis) + '' + xml_output += '' + str(line.allow_chrg_rate) + '' + xml_output += '' + str(line.allow_chrg_qty_uom) + '' + xml_output += '' + str(line.allow_chrg_handling_code) + '' + xml_output += '' + str(line.allow_chrg_handling_description) + '' - #line item acknowledgement + # line item acknowledgement xml_output += '' - xml_output += '' +str(line.edi_line_msg)+ '' - xml_output += '' +str(quantity)+ '' - xml_output += '' +str(uom)+ '' + xml_output += '' + str(line.edi_line_msg) + '' + xml_output += '' + str(quantity) + '' + xml_output += '' + str(uom) + '' xml_output += '002' - xml_output += '' +str(line.edi_est_ship_date)+ '' + xml_output += '' + str(line.edi_est_ship_date) + '' xml_output += 'FCP' - xml_output += '' +str(line.price_unit)+ '' + xml_output += '' + str(line.price_unit) + '' for tax in line.tax_id: xml_output += 'H780' - xml_output += '' +str(1+tax.amount/100*line.price_unit)+ '' - xml_output += '' +str(tax.amount/100)+ '099990000' + xml_output += '' + str(1 + tax.amount / 100 * line.price_unit) + '' + xml_output += '' + str(tax.amount / 100) + '099990000' xml_output += '
' xml_output += '
' - #Summary - xml_output += ''+str(sale_obj.amount_total)+'' - xml_output += '' +str(total_lines)+ '' - xml_output += '' +str(total_qty)+ '' - xml_output += '' +str(total_weight)+ '' + # Summary + xml_output += '' + str(sale_obj.amount_total) + '' + xml_output += '' + str(total_lines) + '' + xml_output += '' + str(total_qty) + '' + xml_output += '' + str(total_weight) + '' xml_output += '0' xml_output += '0' xml_output += '0' @@ -352,7 +352,7 @@ def create_855(self, cr, uid, sale_ids, context=None): xml_output += '' - #Write ASN doc to text file + # Write ASN doc to text file filename = '855_' + today + '%s.txt' % num filename.replace('/', '_') fd = open(OUT_PATH + '/' + filename, 'w') @@ -363,8 +363,8 @@ def create_855(self, cr, uid, sale_ids, context=None): def _create_855_wrapper(self, cr, uid, context=None): - #search for invoices that are ack_yes = True, edi_yes = True and 855_sent_timestamp = False - eligible_orders = self.search(cr, uid, [('ack_yes','=',True),('edi_yes','=',True),('855_sent_timestamp','=',False)], context=context) + # search for invoices that are ack_yes = True, edi_yes = True and 855_sent_timestamp = False + eligible_orders = self.search(cr, uid, [('ack_yes', '=', True), ('edi_yes', '=', True), ('855_sent_timestamp', '=', False)], context=context) return eligible_orders and self.create_855(cr, uid, eligible_orders, context=context) or False @@ -374,7 +374,7 @@ def action_send_855(self, cr, uid, ids, context=None): if context is None: context = {} - #execute the create_855 method + # execute the create_855 method self.create_855(cr, uid, ids, context=None) return True @@ -405,7 +405,7 @@ def _prepare_procurement_group(self): res = super(SaleOrder, self)._prepare_procurement_group() res['trading_partner_id'] = self.trading_partner_id and self.trading_partner_id.id or False - #res['edi_yes'] = self.trading_partner_id and self.edi_yes or False + # res['edi_yes'] = self.trading_partner_id and self.edi_yes or False res['edi_yes'] = True res['asn_shipment'] = self.asn_shipment or 'asn_id' res['ship_to_code'] = self.ship_to_code or '' @@ -432,12 +432,12 @@ def _prepare_invoice(self): overridden to implement custom invoice generation (making sure to call super() to establish a clean extension chain). """ - stock_picking_obj=self.pool['stock.picking'] + stock_picking_obj = self.pool['stock.picking'] pick = False for pick in self.picking_ids: - if pick.picking_type_id.id==2: + if pick.picking_type_id.id == 2: break @@ -574,7 +574,7 @@ def edi_line_status_change(self, cr, uid, ids, order_id, edi_line_msg, order_edi 'ack_yes': fields.boolean('855', readonly=True, help="Will this order have an 855?"), 'edi_est_del_date': fields.date('Est. Del. Date', help="Line Item Estimated Delivery Date"), 'edi_est_ship_date': fields.date('Est. Ship Date', help="Line Item Estimated Shipping Date"), - 'edi_line_msg': fields.selection((('reject','Reject'), ('accept','Accept'), ('backorder','Backorder')),'EDI Line Status'), + 'edi_line_msg': fields.selection((('reject', 'Reject'), ('accept', 'Accept'), ('backorder', 'Backorder')), 'EDI Line Status'), 'ship_not_before_date': fields.date('Do Not Ship Before This Date', help="Do Not Ship Before This Date."), 'cancel_after_date': fields.date('Cancel if Shipped After This Date', help="Cancel if Shipped After This Date."), 'trading_partner_id': fields.many2one('edi.config', 'Trading Partner', help='EDI Configuration information for partner'), @@ -621,7 +621,7 @@ def edi_line_status_change(self, cr, uid, ids, order_id, edi_line_msg, order_edi 'allow_chrg_rate':fields.float('AllowChrgRate', help="Amount of the allowance or charge."), 'allow_chrg_handling_code':fields.char('AllowChrgHandlingCode', help="Code indicating method of handling for an allowance or charge.."), 'allow_chrg_qty_uom':fields.char('AllowChrgQtyUOM', help=""), - 'allow_chrg_handling_description':fields.char('AllowChrgHandlingDescription', help="Free-form textual description of the note."), + 'allow_chrg_handling_description':fields.char('AllowChrgHandlingDescription', help="Free-form textual description of the note."), } def _get_commitment_date(self, cr, uid, line, context=None): @@ -630,4 +630,4 @@ def _get_commitment_date(self, cr, uid, line, context=None): order_datetime = datetime.strptime(order.date_order, DEFAULT_SERVER_DATETIME_FORMAT) dt = order_datetime + timedelta(days=line.delay or 0.0) est_del_date = dt.strftime(DEFAULT_SERVER_DATETIME_FORMAT) - return est_del_date \ No newline at end of file + return est_del_date diff --git a/connector_spscommerce/models/stock.py b/connector_spscommerce/models/stock.py index 70e4c57..b4d0f81 100644 --- a/connector_spscommerce/models/stock.py +++ b/connector_spscommerce/models/stock.py @@ -9,7 +9,7 @@ class product_pricelist(osv.osv): - _inherit= 'product.pricelist' + _inherit = 'product.pricelist' def price_get_wrapper(self, cr, uid, ids, prod_id, qty, partner=None, context=None): price = self.price_rule_get(cr, uid, ids, prod_id, qty, partner=partner, context=context)[ids][0] @@ -163,8 +163,8 @@ def put_in_pack(self, cr, uid, ids, context=None): for operation in operations: - #assign edi_line_num and po_number to pack operation, move by move. - link_ids = stock_move_op_link_obj.search(cr, uid, [('operation_id','=',operation.id)], context=context) or [] + # assign edi_line_num and po_number to pack operation, move by move. + link_ids = stock_move_op_link_obj.search(cr, uid, [('operation_id', '=', operation.id)], context=context) or [] move = False for link_id in link_ids: @@ -191,22 +191,22 @@ def create(self, cr, uid, vals, context=None): if picking.origin: sale_obj = self.pool['sale.order'] - sale_id = sale_obj.search(cr, uid, [('name','=',picking.origin)], context=context) + sale_id = sale_obj.search(cr, uid, [('name', '=', picking.origin)], context=context) - #look back to the sale order and get the edi field values and write them to the picking + # look back to the sale order and get the edi field values and write them to the picking if sale_id: sale_order = sale_obj.browse(cr, uid, sale_id[0], context=context) data = { 'ship_not_before_date': sale_order.ship_not_before_date, 'cancel_after_date': sale_order.cancel_after_date, - 'client_order_ref': sale_order.client_order_ref, + 'client_order_ref': sale_order.client_order_ref, 'edi_yes': sale_order.edi_yes, 'trading_partner_id': sale_order.trading_partner_id.id, 'bol_num': sale_order.bol_num, - #'tracking_number': sale_order.tracking_number, + # 'tracking_number': sale_order.tracking_number, 'scac_code': sale_order.scac_code, - 'ship_to_code': sale_order.ship_to_code, + 'ship_to_code': sale_order.ship_to_code, } picking.write(data) @@ -238,19 +238,19 @@ def get_operations(self, cr, uid, picking_ids, context=None): for picking_id in picking_ids: - #find links between this move and pack operations + # find links between this move and pack operations pack_op_ids = operation_obj.search(cr, uid, [('picking_id', '=', picking_id)], context=context) operations = operation_obj.browse(cr, uid, pack_op_ids, context=context) operations_dict[picking_id] = operations - #loop through quants found in packages for this particular move + # loop through quants found in packages for this particular move for operation in operations: if not operation.result_package_id: continue - #assign edi_line_num and po_number to pack operation, move by move. - link_id = operation.qty_done and stock_move_op_link_obj.search(cr, uid, [('operation_id','=',operation.id)], context=context) or [] + # assign edi_line_num and po_number to pack operation, move by move. + link_id = operation.qty_done and stock_move_op_link_obj.search(cr, uid, [('operation_id', '=', operation.id)], context=context) or [] move = link_id and stock_move_op_link_obj.browse(cr, uid, link_id, context=context).move_id or False package = operation.result_package_id @@ -267,23 +267,23 @@ def create_text_856(self, cr, uid, picking_ids, context=None): for picking in self.browse(cr, uid, picking_ids, context=context): name += picking.name - #Grab the vendor_id and trading_partner_id for EDI in the edi_config. + # Grab the vendor_id and trading_partner_id for EDI in the edi_config. trading_partner_code = picking.trading_partner_id.partner_header_string vendor_code = picking.trading_partner_id.vendor_header_string - #ship_date + # ship_date ship_date = picking.date_done dateship_object = datetime.strptime(ship_date, DEFAULT_SERVER_DATETIME_FORMAT) ship_date = dateship_object.date() ship_date = str(ship_date) - #ship_time + # ship_time ship_time = picking.date_done dateship_object = datetime.strptime(ship_time, DEFAULT_SERVER_DATETIME_FORMAT) ship_time = dateship_object.time() ship_time = str(ship_time) - #Schedule_date + # Schedule_date schedule_date = picking.est_del_date if not schedule_date: schedule_date = picking.max_date @@ -293,7 +293,7 @@ def create_text_856(self, cr, uid, picking_ids, context=None): schedule_date = dateship_object.date() schedule_date = str(schedule_date) - #Schedule_time + # Schedule_time schedule_time = picking.est_del_date if not schedule_time: schedule_time = picking.max_date @@ -303,7 +303,7 @@ def create_text_856(self, cr, uid, picking_ids, context=None): schedule_time = dateship_object.time() schedule_time = str(schedule_time) - #Notice_date + # Notice_date notice_date = picking.ship_not_before_date if not notice_date: notice_date = picking.min_date @@ -569,13 +569,13 @@ def create_text_856(self, cr, uid, picking_ids, context=None): shipment_dict.get('Shipment').update(orderlevel_dict) shipment_dict.get('Shipment').get('OrderLevel').update(packlevel_dict) - #Sublines + # Sublines sublines_list = {'Sublines': []} total_lines = 0 total_qty = 0 total_weight = 0 - #for each sub line + # for each sub line for line in picking.move_lines: total_lines += 1 total_qty += line.product_qty @@ -606,7 +606,7 @@ def create_text_856(self, cr, uid, picking_ids, context=None): subline_dict.get('Subline').update(pickingline_dict) sublines_list.get('Sublines').append(subline_dict) - #shipment_dict.get('Shipment').get('OrderLevel').get('PackLevel').get('ItemLevel').update(sublines_list) + # shipment_dict.get('Shipment').get('OrderLevel').get('PackLevel').get('ItemLevel').update(sublines_list) shipment_dict.get('Shipment').get('OrderLevel').update(sublines_list) # summary @@ -622,11 +622,11 @@ def create_text_856(self, cr, uid, picking_ids, context=None): shipments_list.get('Shipments').append(shipment_dict) processed += 1 - #convert dictionary to xml + # convert dictionary to xml xml = dicttoxml.dicttoxml(shipments_list, attr_type=False, root=False) - xml = xml.replace('','').replace('','') - xml = xml.replace('','').replace('','') - #Write ASN doc to text file + xml = xml.replace('', '').replace('', '') + xml = xml.replace('', '').replace('', '') + # Write ASN doc to text file name = re.findall('\d+', name)[0] filename = '856_' + today + '%s.xml' % name filename.replace('/', '_') @@ -637,8 +637,8 @@ def create_text_856(self, cr, uid, picking_ids, context=None): def _create_856_wrapper(self, cr, uid, context=None): - #search for invoices that are edi_yes = True and 856_sent_timestamp = False - eligible_pickings = self.search(cr, uid, [('edi_yes','=',True),('856_sent_timestamp','=',False)], context=context) + # search for invoices that are edi_yes = True and 856_sent_timestamp = False + eligible_pickings = self.search(cr, uid, [('edi_yes', '=', True), ('856_sent_timestamp', '=', False)], context=context) return eligible_pickings and self.create_text_856(cr, uid, eligible_pickings, context=context) or False # done as a server action @@ -654,4 +654,4 @@ def action_create_text_856(self, cr, uid, ids, context=None): # process orders to write 856 processed = self.create_text_856(cr, uid, ids, context=context) - return (toprocess - processed) \ No newline at end of file + return (toprocess - processed) diff --git a/connector_spscommerce/models/stock_move.py b/connector_spscommerce/models/stock_move.py index 2219c41..f50c952 100644 --- a/connector_spscommerce/models/stock_move.py +++ b/connector_spscommerce/models/stock_move.py @@ -46,15 +46,15 @@ def _picking_assign(self, cr, uid, move_ids, context=None): edi_yes = move.procurement_id.edi_yes if edi_yes: res = {} - res['trading_partner_id'] =\ + res['trading_partner_id'] = \ move.procurement_id.trading_partner_id and\ move.procurement_id.trading_partner_id.id or False res['edi_yes'] = move.procurement_id.trading_partner_id\ and edi_yes or False - res['ship_not_before_date'] =\ + res['ship_not_before_date'] = \ move.procurement_id.trading_partner_id and\ move.procurement_id.ship_not_before_date or False - res['cancel_after_date'] =\ + res['cancel_after_date'] = \ move.procurement_id.trading_partner_id and\ move.procurement_id.cancel_after_date or False picking.write(res) From b500412ae292366f363854265a8910776c858ae3 Mon Sep 17 00:00:00 2001 From: Sandip Mangukiya Date: Mon, 20 Aug 2018 17:36:41 +0530 Subject: [PATCH 3/5] Code Formatting --- connector_spscommerce/edi_scripts/edi_850.py | 4 ++-- connector_spscommerce/edi_scripts/edi_process_in.py | 8 ++++---- connector_spscommerce/edi_scripts/monitor.py | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/connector_spscommerce/edi_scripts/edi_850.py b/connector_spscommerce/edi_scripts/edi_850.py index 20ce563..002a9ca 100644 --- a/connector_spscommerce/edi_scripts/edi_850.py +++ b/connector_spscommerce/edi_scripts/edi_850.py @@ -542,7 +542,7 @@ def process_record(sock, uid, record, partner_rec, config_rec): print "Automatic workflow is: " + str(config_rec['auto_workflow']) automatic_workflow_id = config_rec['auto_workflow'] # use the 850 PO info to create a sales order - sale_id, src_location_id, pricelist_id =\ + sale_id, src_location_id, pricelist_id = \ create_sale_order(sock, uid, record['date'], record['po_num'], @@ -565,7 +565,7 @@ def process_record(sock, uid, record, partner_rec, config_rec): sale_name = get_sale_name(sock, uid, sale_id) # create SO LINE from dictionary we just created - product_ids, sale_line_ids =\ + product_ids, sale_line_ids = \ create_so_lines(sock, uid, sale_id, record['upc'], record['sku'], diff --git a/connector_spscommerce/edi_scripts/edi_process_in.py b/connector_spscommerce/edi_scripts/edi_process_in.py index 467e876..ef21163 100644 --- a/connector_spscommerce/edi_scripts/edi_process_in.py +++ b/connector_spscommerce/edi_scripts/edi_process_in.py @@ -458,9 +458,9 @@ def get_order_header(order_header): import """ return\ - order_header['Department'],\ - order_header['PurchaseOrderNumber'],\ - order_header['TradingPartnerId'],\ + order_header['Department'], \ + order_header['PurchaseOrderNumber'], \ + order_header['TradingPartnerId'], \ order_header['PurchaseOrderDate'] @@ -589,7 +589,7 @@ def create_sale_order(sock, uid, order_data): if partner_info['id']: partner_invoice_id = partner_info['id'] partner_shipping_id = partner_info['id'] - print "datetime.now().strftime('%Y-%m-%d %H:%M:%S')",\ + print "datetime.now().strftime('%Y-%m-%d %H:%M:%S')", \ datetime.now().strftime('%Y-%m-%d %H:%M:%S') sale_hash = { 'incoterm': 14, # hardcoded to "Delivery at Place" diff --git a/connector_spscommerce/edi_scripts/monitor.py b/connector_spscommerce/edi_scripts/monitor.py index 2acfdec..0a68031 100644 --- a/connector_spscommerce/edi_scripts/monitor.py +++ b/connector_spscommerce/edi_scripts/monitor.py @@ -83,7 +83,7 @@ def process_files(ftp, SOURCE_DIR, LOCAL_SOURCE_DIR, DEST_DIR, LOCAL_DEST_DIR): try: ftp.rename(SOURCE_DIR + filename, REMOTE_ARCHIVE + filename) - logging.info('Successfully transfered %s' % + logging.info('Successfully transfered %s' % filename + 'from remote host') print 'Successful Transfer of: ' + filename + \ ' from remote host' @@ -99,7 +99,7 @@ def process_files(ftp, SOURCE_DIR, LOCAL_SOURCE_DIR, DEST_DIR, LOCAL_DEST_DIR): try: shutil.move(LOCAL_SOURCE_DIR + filename, ARCHIVE) - logging.info('Successfully transfered %s' % + logging.info('Successfully transfered %s' % filename + 'to remote host') print 'Successful Transfer of: ' + filename + ' to remote host' except Exception: @@ -130,7 +130,7 @@ def transfer_files(ftp, files, SOURCE_DIR, DEST_DIR, transfer_type): except Exception: res = False - logging.error('Error transferring ' + filename + + logging.error('Error transferring ' + filename + ' using transfer type: ' + transfer_type) return res From 71dd901a55ea40b191ac80423cc34718265db052 Mon Sep 17 00:00:00 2001 From: Sandip Mangukiya Date: Mon, 20 Aug 2018 18:27:26 +0530 Subject: [PATCH 4/5] Improve configuration details to more generic --- .../edi_scripts/connect_info.py | 16 ++++++++++------ connector_spscommerce/readme/CONFIGURE.rst | 6 +++++- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/connector_spscommerce/edi_scripts/connect_info.py b/connector_spscommerce/edi_scripts/connect_info.py index 8aec630..188ca97 100644 --- a/connector_spscommerce/edi_scripts/connect_info.py +++ b/connector_spscommerce/edi_scripts/connect_info.py @@ -1,9 +1,13 @@ +# -*- coding: utf-8 -*- +# Copyright (c) Open Source Integrators +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + # Set the parameters appropriately before attempting EDI connection -# OERP Constants -USERNAME = 'admin' -PWD = 'admin' -DBNAME = 'Medico-Master-04032016' -ERP_WWW = "http://0.0.0.0:8069" +# Odoo Constants +USERNAME = '' +PWD = '' +DBNAME = '' +ERP_WWW = "http://:" or "ERP Domain" EOL_MARKER = '~' DEBUG = True -IN_PATH = '/home/ram/Desktop/spscommercetask/Test/' +IN_PATH = '/opt/local-addons/connector_spscommerce/test/' diff --git a/connector_spscommerce/readme/CONFIGURE.rst b/connector_spscommerce/readme/CONFIGURE.rst index a2546bb..9b281a0 100644 --- a/connector_spscommerce/readme/CONFIGURE.rst +++ b/connector_spscommerce/readme/CONFIGURE.rst @@ -1,4 +1,8 @@ To configure this module, you need to add a cron job: -`*/15 * * * * python /opt/local-addons/connector_spscommerce/edi_scripts/edi_process_in.py` \ No newline at end of file +`*/15 * * * * python /opt/local-addons/connector_spscommerce/edi_scripts/edi_process_in.py` + +Set the parameters appropriately before attempting EDI connection at below: + +`*/15 * * * * python /opt/local-addons/connector_spscommerce/edi_scripts/connect_info.py` \ No newline at end of file From f7d10ecc64e4504376ac03c16c3c95def1158abe Mon Sep 17 00:00:00 2001 From: Nikul Chaudhary Date: Mon, 3 Dec 2018 18:12:57 +0530 Subject: [PATCH 5/5] [MIG] Migrated connector_spscommerce v11 --- connector_spscommerce/__init__.py | 2 - .../{__openerp__.py => __manifest__.py} | 9 +- .../edi_scripts/csv_parser.py | 4 +- connector_spscommerce/edi_scripts/edi_850.py | 48 +- .../edi_scripts/edi_process_in.py | 286 ++-- connector_spscommerce/edi_scripts/monitor.py | 18 +- connector_spscommerce/edi_scripts/test.py | 18 +- connector_spscommerce/models/__init__.py | 21 +- .../models/account_invoice.py | 870 +++++++---- connector_spscommerce/models/edi_config.py | 119 +- connector_spscommerce/models/procurement.py | 113 +- connector_spscommerce/models/purchase.py | 18 +- connector_spscommerce/models/res_company.py | 14 + connector_spscommerce/models/res_partner.py | 21 + connector_spscommerce/models/sale.py | 1349 +++++++++++------ connector_spscommerce/models/stock.py | 686 +-------- connector_spscommerce/models/stock_move.py | 154 +- connector_spscommerce/models/stock_picking.py | 760 ++++++++++ connector_spscommerce/readme/CONTRIBUTORS.rst | 2 + .../views/account_invoice_view.xml | 145 +- .../views/company_config_settings_view.xml | 83 +- connector_spscommerce/views/res_company.xml | 16 + connector_spscommerce/views/res_partner.xml | 37 + connector_spscommerce/views/sale_view.xml | 191 ++- connector_spscommerce/views/stock_view.xml | 271 ++-- 25 files changed, 3158 insertions(+), 2097 deletions(-) rename connector_spscommerce/{__openerp__.py => __manifest__.py} (79%) create mode 100644 connector_spscommerce/models/res_company.py create mode 100644 connector_spscommerce/models/res_partner.py create mode 100644 connector_spscommerce/models/stock_picking.py create mode 100644 connector_spscommerce/views/res_company.xml create mode 100644 connector_spscommerce/views/res_partner.xml diff --git a/connector_spscommerce/__init__.py b/connector_spscommerce/__init__.py index be3a8a1..69f7bab 100644 --- a/connector_spscommerce/__init__.py +++ b/connector_spscommerce/__init__.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- -# Copyright (c) Open Source Integrators # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). from . import models diff --git a/connector_spscommerce/__openerp__.py b/connector_spscommerce/__manifest__.py similarity index 79% rename from connector_spscommerce/__openerp__.py rename to connector_spscommerce/__manifest__.py index b4b1e41..c677531 100644 --- a/connector_spscommerce/__openerp__.py +++ b/connector_spscommerce/__manifest__.py @@ -5,20 +5,23 @@ { 'name': 'EDI Integration with SPS Commerce', 'summary': 'Integrate with retail stores using SPS Commerce', - 'version': '9.0.1.0.0', + 'version': '11.0.1.0.0', 'license': 'AGPL-3', 'author': 'Open Source Integrators, Odoo Community Association (OCA)', + 'website': 'https://github.com/OCA/connector-spscommerce', 'depends': [ - 'procurement', 'account', 'delivery', 'sale_stock', + 'purchase', 'sale_automatic_workflow', ], - 'data':[ + 'data': [ 'views/sale_view.xml', 'views/stock_view.xml', 'views/company_config_settings_view.xml', + 'views/res_company.xml', + 'views/res_partner.xml', 'views/account_invoice_view.xml', ], 'installable': True, diff --git a/connector_spscommerce/edi_scripts/csv_parser.py b/connector_spscommerce/edi_scripts/csv_parser.py index 346601e..f6ced76 100644 --- a/connector_spscommerce/edi_scripts/csv_parser.py +++ b/connector_spscommerce/edi_scripts/csv_parser.py @@ -54,11 +54,11 @@ def parse_csv(file): if count == 1 and row[0] != 'H': - print """INFO: ***** FAILURE - YOUR FILE DOES NOT HAVE THE REQUIRED + print("""INFO: ***** FAILURE - YOUR FILE DOES NOT HAVE THE REQUIRED FIRST ROW WITH COLUMN HEADINGS: EDI Loop | Order line | Partner | PO # | Date/UPC | code?/product desc | quantity | UOM Price ***** - """ + """) break if count > 2 and row[0] == '': diff --git a/connector_spscommerce/edi_scripts/edi_850.py b/connector_spscommerce/edi_scripts/edi_850.py index 002a9ca..18353f4 100644 --- a/connector_spscommerce/edi_scripts/edi_850.py +++ b/connector_spscommerce/edi_scripts/edi_850.py @@ -62,7 +62,7 @@ def edi_config_id( edi = ids[0] except Exception: - print "There are no trading partners to process" + print("There are no trading partners to process") pass return edi @@ -89,8 +89,8 @@ def get_config(sock, uid, edi): rec = record['archive_path'] return record except Exception: - print "There are no trading partners to process, paths not set or EOL" \ - " marker not defined." + print("""There are no trading partners to process, paths not set or + EOL marker not defined.""") pass @@ -252,7 +252,7 @@ def get_shop_location(sock, uid, warehouse_id): warehouse_id, ['lot_stock_id']) - print 'shop stock loc: ' + str(src_location_id) + print('shop stock loc: ' + str(src_location_id)) return src_location_id @@ -291,8 +291,8 @@ def rename_order_pol(order_policy): if order_pol: return order_pol else: - print "FAILURE - ORDER POLICY MUST BE: On Demand, On Delivery Order," \ - " OR Before Delivery" + print("""FAILURE - ORDER POLICY MUST BE: On Demand, On Delivery Order, + OR Before Delivery""") def check_po_number(sock, uid, client_order_ref, partner_id): @@ -319,8 +319,8 @@ def check_po_number(sock, uid, client_order_ref, partner_id): # if sale_ids from the above search, then po number is duplicated, warn # user, and do not input the sale order if sale_ids: - print "This is a duplicate PO and will not be pushed into the" \ - " system." + print("""This is a duplicate PO and will not be pushed + into the system.""") result = True return result @@ -395,7 +395,7 @@ def create_sale_order( 'edi_est_so_ship_date': datetime.now().strftime('%Y-%m-%d') } - print "Attempting to create %s" % sale_hash + print("Attempting to create %s" % sale_hash) sale_id = sock.execute(DBNAME, uid, PWD, 'sale.order', 'create', sale_hash) return sale_id, src_location_id, pricelist_id @@ -408,7 +408,7 @@ def search_product(sock, uid, product_upc, product_sku): product_id = ids[0] except Exception: product_id = 'no_exist' - print 'product does not exist' + print('product does not exist') return product_id @@ -517,12 +517,12 @@ def create_so_lines( sale_line_ids.append(line_id) except Exception: - print "Could not add the order line", sys.exc_info()[0] + print("Could not add the order line", sys.exc_info()[0]) else: - print "INFO: ***** FAILURE TO FIND PRODUCT WITH UPC CODE: " + \ - str(product_code) + print("""INFO: ***** FAILURE TO FIND PRODUCT WITH UPC CODE: + """ + str(product_code)) products_not_found = products_not_found + '\n Line not input: ' + \ qty + ' ' + uom + ' of ' + product_code @@ -539,7 +539,7 @@ def process_record(sock, uid, record, partner_rec, config_rec): # We're using fullfillment_channel to store Sale Automatic Workflow ID per # customer, this is attached to the order - print "Automatic workflow is: " + str(config_rec['auto_workflow']) + print("Automatic workflow is: " + str(config_rec['auto_workflow'])) automatic_workflow_id = config_rec['auto_workflow'] # use the 850 PO info to create a sales order sale_id, src_location_id, pricelist_id = \ @@ -611,7 +611,7 @@ def main(sock, uid, order_dict, in_path): # process record and input sale order with multiple order lines in # OpenERP - print "Processing PO#: " + str(order_dict['po_num']) + print("Processing PO#: " + str(order_dict['po_num'])) if not check_po_number( sock, uid, @@ -623,20 +623,20 @@ def main(sock, uid, order_dict, in_path): sock, uid, order_dict, partner_rec, config) except Exception: - print "Cannot process order. Sale order or order lines not" \ - " created. ", sys.exc_info() + print("""Cannot process order.Sale order or order + lines not created. """, sys.exc_info()) - print "Sale added id: " + str(sale_id) + print("Sale added id: " + str(sale_id)) else: - print 'There is no trading partner that matches the partner header' \ - ' string in the 850 file.' - print 'Please verify that there are trading partners defined and that' \ - ' the header strings are correctly defined on them.' + print('''There is no trading partner that matches the partner header + string in the 850 file.''') + print('''Please verify that there are trading partners defined and + that the header strings are correctly defined on them.''') return sale_id if __name__ == '__main__': - print 'Process: EDI In - Starting' + print('Process: EDI In - Starting') main() - print 'Process: EDI In - Ending' + print('Process: EDI In - Ending') diff --git a/connector_spscommerce/edi_scripts/edi_process_in.py b/connector_spscommerce/edi_scripts/edi_process_in.py index ef21163..2243558 100644 --- a/connector_spscommerce/edi_scripts/edi_process_in.py +++ b/connector_spscommerce/edi_scripts/edi_process_in.py @@ -42,7 +42,8 @@ 2013-11-15 90 1845.08 - Must be paid in full by end of year + Must be paid in full by end of + year AB 999888777 @@ -113,7 +114,8 @@ 1 5.0 02 - This will cover the cost of shipping + This will cover the cost of shipping + @@ -127,7 +129,8 @@ 12345678901234 98765432101 51456-299 - 999-0-555-22222-0 + 999-0-555-22222-0 + VC ABC-456789 @@ -174,7 +177,8 @@ CAFA3 - REPEAT LOGO PREVIOUS ORDER + REPEAT LOGO PREVIOUS ORDER + N @@ -193,7 +197,8 @@ 12345678901234 98765432101 51456-299 - 999-0-555-22222-0 + 999-0-555-22222-0 + N5 ABC-456789 @@ -202,7 +207,8 @@ Small C-999 Fire Truck Red - Faux Fabric + Faux Fabric + 5 PP 5.85 @@ -252,7 +258,8 @@ 1 5.0 01 - This will cover the cost of shipping + This will cover the cost of shipping + @@ -275,7 +282,7 @@ def connect_oerp(): sock_common = xmlrpclib.ServerProxy(ERP_WWW + '/xmlrpc/common') uid = sock_common.login(DBNAME, USERNAME, PWD) - print "XMLRPC Connection: SUCCESS - SERVER HAS AUTHENTICATED A LOGIN" + print("XMLRPC Connection: SUCCESS - SERVER HAS AUTHENTICATED A LOGIN") return sock, uid @@ -290,88 +297,142 @@ def parse_csv(sock, uid, file): # improve process in for order in orders['Orders']: - record['address_type_code'] = order['Order']['Header']['Address']['AddressTypeCode'] - record['location_code_qualifier'] = order['Order']['Header']['Address']['LocationCodeQualifier'] - record['address_location_number'] = order['Order']['Header']['Address']['AddressLocationNumber'] - record['address_name'] = order['Order']['Header']['Address']['AddressName'] - record['address_alternate_name'] = order['Order']['Header']['Address']['AddressAlternateName'] + record['address_type_code'] =\ + order['Order']['Header']['Address']['AddressTypeCode'] + record['location_code_qualifier'] =\ + order['Order']['Header']['Address']['LocationCodeQualifier'] + record['address_location_number'] =\ + order['Order']['Header']['Address']['AddressLocationNumber'] + record['address_name'] =\ + order['Order']['Header']['Address']['AddressName'] + record['address_alternate_name'] =\ + order['Order']['Header']['Address']['AddressAlternateName'] record['address1'] = order['Order']['Header']['Address']['Address1'] record['address2'] = order['Order']['Header']['Address']['Address2'] record['city'] = order['Order']['Header']['Address']['City'] record['state'] = order['Order']['Header']['Address']['State'] - record['postal_code'] = order['Order']['Header']['Address']['PostalCode'] + record['postal_code'] =\ + order['Order']['Header']['Address']['PostalCode'] record['country'] = order['Order']['Header']['Address']['Country'] record['contact'] = {} - record['contact']['contact_type_code'] = order['Order']['Header']['Address']['Contact']['ContactTypeCode'] - record['contact']['contact_name'] = order['Order']['Header']['Address']['Contact']['ContactName'] - record['contact']['primary_phone'] = order['Order']['Header']['Address']['Contact']['PrimaryPhone'] - record['contact']['primary_fax'] = order['Order']['Header']['Address']['Contact']['PrimaryFax'] - record['contact']['primary_email'] = order['Order']['Header']['Address']['Contact']['PrimaryEmail'] + record['contact']['contact_type_code'] =\ + order['Order']['Header']['Address']['Contact']['ContactTypeCode'] + record['contact']['contact_name'] =\ + order['Order']['Header']['Address']['Contact']['ContactName'] + record['contact']['primary_phone'] =\ + order['Order']['Header']['Address']['Contact']['PrimaryPhone'] + record['contact']['primary_fax'] =\ + order['Order']['Header']['Address']['Contact']['PrimaryFax'] + record['contact']['primary_email'] =\ + order['Order']['Header']['Address']['Contact']['PrimaryEmail'] record['date'] = order['Order']['Header']['Date']['Date1'] record['time'] = order['Order']['Header']['Date']['Time1'] record['edi_error'] = False record['edi_yes'] = False record['ack_yes'] = True - record['855_replace'] = True + record['replace'] = True record['supplier_code'] = '123' record['ship_not_before_date'] = datetime.now().strftime('%Y-%m-%d') record['cancel_after_date'] = datetime.now().strftime('%Y-%m-%d') - record['trading_partner_id'] = order['Order']['Header']['OrderHeader']['TradingPartnerId'] + record['trading_partner_id'] =\ + order['Order']['Header']['OrderHeader']['TradingPartnerId'] record['scac_code'] = '' record['bol_num'] = '' record['asn_shipment'] = '' record['tracking_num'] = '' - record['tset_purpose_code'] = order['Order']['Header']['OrderHeader']['TsetPurposeCode'] - record['purchase_order_number'] = order['Order']['Header']['OrderHeader']['PurchaseOrderNumber'] - record['purchase_order_type_code'] = order['Order']['Header']['OrderHeader']['PurchaseOrderTypeCode'] - record['po_type_description'] = order['Order']['Header']['OrderHeader']['POTypeDescription'] - record['ship_complete_code'] = order['Order']['Header']['OrderHeader']['ShipCompleteCode'] - record['department'] = order['Order']['Header']['OrderHeader']['Department'] - record['division'] = order['Order']['Header']['OrderHeader']['Division'] - record['promotion_deal_number'] = order['Order']['Header']['OrderHeader']['PromotionDealNumber'] - record['terms_type'] = order['Order']['Header']['PaymentTerms']['TermsType'] - record['terms_basis_date_code'] = order['Order']['Header']['PaymentTerms']['TermsBasisDateCode'] - record['terms_discount_percentage'] = order['Order']['Header']['PaymentTerms']['TermsDiscountPercentage'] - record['terms_discount_due_days'] = order['Order']['Header']['PaymentTerms']['TermsDiscountDueDays'] - record['terms_net_due_days'] = order['Order']['Header']['PaymentTerms']['TermsNetDueDays'] - record['payment_method_code'] = order['Order']['Header']['PaymentTerms']['PaymentMethodCode'] - record['fob_pay_code'] = order['Order']['Header']['FOBRelatedInstruction']['FOBPayCode'] - record['fob_location_qualifier'] = order['Order']['Header']['FOBRelatedInstruction']['FOBLocationQualifier'] - record['fob_location_description'] = order['Order']['Header']['FOBRelatedInstruction']['FOBLocationDescription'] + record['tset_purpose_code'] =\ + order['Order']['Header']['OrderHeader']['TsetPurposeCode'] + record['purchase_order_number'] =\ + order['Order']['Header']['OrderHeader']['PurchaseOrderNumber'] + record['purchase_order_type_code'] =\ + order['Order']['Header']['OrderHeader']['PurchaseOrderTypeCode'] + record['po_type_description'] =\ + order['Order']['Header']['OrderHeader']['POTypeDescription'] + record['ship_complete_code'] =\ + order['Order']['Header']['OrderHeader']['ShipCompleteCode'] + record['department'] =\ + order['Order']['Header']['OrderHeader']['Department'] + record['division'] =\ + order['Order']['Header']['OrderHeader']['Division'] + record['promotion_deal_number'] =\ + order['Order']['Header']['OrderHeader']['PromotionDealNumber'] + record['terms_type'] =\ + order['Order']['Header']['PaymentTerms']['TermsType'] + record['terms_basis_date_code'] =\ + order['Order']['Header']['PaymentTerms']['TermsBasisDateCode'] + record['terms_discount_percentage'] =\ + order['Order']['Header']['PaymentTerms']['TermsDiscountPercentage'] + record['terms_discount_due_days'] =\ + order['Order']['Header']['PaymentTerms']['TermsDiscountDueDays'] + record['terms_net_due_days'] =\ + order['Order']['Header']['PaymentTerms']['TermsNetDueDays'] + record['payment_method_code'] =\ + order['Order']['Header']['PaymentTerms']['PaymentMethodCode'] + record['fob_pay_code'] =\ + order['Order']['Header']['FOBRelatedInstruction']['FOBPayCode'] + record['fob_location_qualifier'] =\ + order['Order']['Header']['FOBRelatedInstruction' + ]['FOBLocationQualifier'] + record['fob_location_description'] =\ + order['Order']['Header']['FOBRelatedInstruction' + ]['FOBLocationDescription'] record['fob_title_passage_code'] = '' record['fob_title_passage_location'] = '' - record['carrier_trans_method_code'] = order['Order']['Header']['CarrierInformation']['CarrierTransMethodCode'] - record['carrier_alpha_code'] = order['Order']['Header']['CarrierInformation']['CarrierAlphaCode'] - record['carrier_routing'] = order['Order']['Header']['CarrierInformation']['CarrierRouting'] + record['carrier_trans_method_code'] =\ + order['Order']['Header']['CarrierInformation' + ]['CarrierTransMethodCode'] + record['carrier_alpha_code'] =\ + order['Order']['Header']['CarrierInformation']['CarrierAlphaCode'] + record['carrier_routing'] =\ + order['Order']['Header']['CarrierInformation']['CarrierRouting'] record['routing_sequence_code'] = '' record['service_level_code'] = '' - record['reference_qual'] = order['Order']['Header']['Reference']['ReferenceQual'] - record['reference_id'] = order['Order']['Header']['Reference']['ReferenceID'] - record['ref_description'] = order['Order']['Header']['Reference']['Description'] + record['reference_qual'] =\ + order['Order']['Header']['Reference']['ReferenceQual'] + record['reference_id'] =\ + order['Order']['Header']['Reference']['ReferenceID'] + record['ref_description'] =\ + order['Order']['Header']['Reference']['Description'] record['note_code'] = order['Order']['Header']['Notes']['NoteCode'] - record['note_information_field'] = order['Order']['Header']['Notes']['NoteInformationField'] - record['allow_chrg_indicator'] = order['Order']['Header']['ChargesAllowances']['AllowChrgIndicator'] - record['allow_chrg_code'] = order['Order']['Header']['ChargesAllowances']['AllowChrgCode'] + record['note_information_field'] =\ + order['Order']['Header']['Notes']['NoteInformationField'] + record['allow_chrg_indicator'] =\ + order['Order']['Header']['ChargesAllowances']['AllowChrgIndicator'] + record['allow_chrg_code'] =\ + order['Order']['Header']['ChargesAllowances']['AllowChrgCode'] record['allow_chrg_agency_code'] = '' record['allow_chrg_agency'] = '' - record['allow_chrg_amt'] = order['Order']['Header']['ChargesAllowances']['AllowChrgAmt'] - record['allow_chrg_percent_qual'] = order['Order']['Header']['ChargesAllowances']['AllowChrgPercentQual'] - record['allow_chrg_percent'] = order['Order']['Header']['ChargesAllowances']['AllowChrgPercent'] - record['allow_chrg_handling_code'] = order['Order']['Header']['ChargesAllowances']['AllowChrgHandlingCode'] + record['allow_chrg_amt'] =\ + order['Order']['Header']['ChargesAllowances']['AllowChrgAmt'] + record['allow_chrg_percent_qual'] =\ + order['Order']['Header']['ChargesAllowances' + ]['AllowChrgPercentQual'] + record['allow_chrg_percent'] =\ + order['Order']['Header']['ChargesAllowances']['AllowChrgPercent'] + record['allow_chrg_handling_code'] =\ + order['Order']['Header']['ChargesAllowances' + ]['AllowChrgHandlingCode'] record['reference_identification'] = '' - record['allow_chrg_handling_description'] = order['Order']['Header']['ChargesAllowances']['AllowChrgHandlingDescription'] + record['allow_chrg_handling_description'] =\ + order['Order']['Header']['ChargesAllowances' + ]['AllowChrgHandlingDescription'] record['order_line'] = {} for line in orders['Orders']['Order']['LineItems']: order_line_data = { - 'product_qty': orders['Orders']['Order']['LineItems'][line]['OrderLine']['OrderQty'], - 'product_uom': orders['Orders']['Order']['LineItems'][line]['OrderLine']['OrderQtyUOM'], - 'sku': orders['Orders']['Order']['LineItems'][line]['OrderLine']['VendorPartNumber'], + 'product_qty': orders['Orders']['Order']['LineItems'][line] + ['OrderLine']['OrderQty'], + 'product_uom': orders['Orders']['Order']['LineItems'][line] + ['OrderLine']['OrderQtyUOM'], + 'sku': orders['Orders']['Order']['LineItems'][line] + ['OrderLine']['VendorPartNumber'], 'edi_yes': False, 'asn_shipment': '123', 'po_number': '123', - 'buyer_part_number': orders['Orders']['Order']['LineItems'][line]['OrderLine']['BuyerPartNumber'], - 'edi_line_num': orders['Orders']['Order']['LineItems'][line]['OrderLine']['LineSequenceNumber'], + 'buyer_part_number': orders['Orders']['Order']['LineItems'] + [line]['OrderLine']['BuyerPartNumber'], + 'edi_line_num': orders['Orders']['Order']['LineItems'][line] + ['OrderLine']['LineSequenceNumber'], 'edi_line_qty': '123', 'ack_yes': '123', 'edi_est_del_date': datetime.now().strftime('%Y-%m-%d'), @@ -379,59 +440,90 @@ def parse_csv(sock, uid, file): 'edi_line_msg': '123', 'ship_not_before_date': datetime.now().strftime('%Y-%m-%d'), 'cancel_after_date': datetime.now().strftime('%Y-%m-%d'), - 'trading_partner_id': orders['Orders']['Order']['Header']['OrderHeader']['TradingPartnerId'], + 'trading_partner_id': orders['Orders']['Order']['Header'] + ['OrderHeader']['TradingPartnerId'], 'edi_intransit_qty': 0, 'edi_outgoing_qty': 0, 'edi_avlforsale_qty': 0, - 'vendor_part_number': orders['Orders']['Order']['LineItems'][line]['OrderLine']['VendorPartNumber'], - 'consumer_package_code': orders['Orders']['Order']['LineItems'][line]['OrderLine']['ConsumerPackageCode'], - 'gtin': orders['Orders']['Order']['LineItems'][line]['OrderLine']['GTIN'], - 'upc_case_code': orders['Orders']['Order']['LineItems'][line]['OrderLine']['UPCCaseCode'], - 'purchase_price_basis': orders['Orders']['Order']['LineItems'][line]['OrderLine']['PurchasePrice'], - 'product_size_code': orders['Orders']['Order']['LineItems'][line]['OrderLine']['ProductSizeCode'], - 'product_size_description': orders['Orders']['Order']['LineItems'][line]['OrderLine']['ProductSizeDescription'], - 'product_color_code': orders['Orders']['Order']['LineItems'][line]['OrderLine']['ProductColorCode'], - 'product_color_description': orders['Orders']['Order']['LineItems'][line]['OrderLine']['ProductColorDescription'], - # 'product_material_code':orders['Orders']['Order']['LineItems'][line]['OrderLine']['ProductMaterialCode'], - 'product_material_description': orders['Orders']['Order']['LineItems'][line]['OrderLine']['ProductMaterialDescription'], - 'department': orders['Orders']['Order']['LineItems'][line]['OrderLine']['Department'], - 'classs': orders['Orders']['Order']['LineItems'][line]['OrderLine']['Class'], - 'price_type_id_code': orders['Orders']['Order']['LineItems'][line]['PriceInformation']['PriceTypeIDCode'], - 'edi_line_num': orders['Orders']['Order']['LineItems'][line]['OrderLine']['LineSequenceNumber'], - # 'multiple_price_quantity':orders['Orders']['Order']['LineItems'][line]['PriceInformation']['MultiplePriceQuantity'], - # 'class_of_trade_code':orders['Orders']['Order']['LineItems'][line]['PriceInformation']['ClassOfTradeCode'], - 'item_description_type': orders['Orders']['Order']['LineItems'][line]['ProductOrItemDescription']['ItemDescriptionType'], + 'vendor_part_number': orders['Orders']['Order']['LineItems'] + [line]['OrderLine']['VendorPartNumber'], + 'consumer_package_code': orders['Orders']['Order']['LineItems'] + [line]['OrderLine']['ConsumerPackageCode'], + 'gtin': orders['Orders']['Order']['LineItems'][line] + ['OrderLine']['GTIN'], + 'upc_case_code': orders['Orders']['Order']['LineItems'][line] + ['OrderLine']['UPCCaseCode'], + 'purchase_price_basis': orders['Orders']['Order']['LineItems'] + [line]['OrderLine']['PurchasePrice'], + 'product_size_code': orders['Orders']['Order']['LineItems'] + [line]['OrderLine']['ProductSizeCode'], + 'product_size_description': orders['Orders']['Order'] + ['LineItems'][line]['OrderLine']['ProductSizeDescription'], + 'product_color_code': orders['Orders']['Order']['LineItems'] + [line]['OrderLine']['ProductColorCode'], + 'product_color_description': orders['Orders']['Order'] + ['LineItems'][line]['OrderLine']['ProductColorDescription'], + # 'product_material_code':orders['Orders']['Order'] + # ['LineItems'][line]['OrderLine']['ProductMaterialCode'], + 'product_material_description': orders['Orders']['Order'] + ['LineItems'][line]['OrderLine']['ProductMaterialDescription'], + 'department': orders['Orders']['Order']['LineItems'][line] + ['OrderLine']['Department'], + 'classs': orders['Orders']['Order']['LineItems'][line] + ['OrderLine']['Class'], + 'price_type_id_code': orders['Orders']['Order']['LineItems'] + [line]['PriceInformation']['PriceTypeIDCode'], + 'edi_line_num': orders['Orders']['Order']['LineItems'][line] + ['OrderLine']['LineSequenceNumber'], + # 'multiple_price_quantity':orders['Orders']['Order'] + # ['LineItems'][line]['PriceInformation'] + # ['MultiplePriceQuantity'], + # 'class_of_trade_code':orders['Orders']['Order']['LineItems' + # [line]['PriceInformation']['ClassOfTradeCode'], + 'item_description_type': orders['Orders']['Order']['LineItems'] + [line]['ProductOrItemDescription']['ItemDescriptionType'], 'product_characteristic_code': '', 'agency_qualifier_code': '', 'product_description_code': '', - 'pack_qualifier': orders['Orders']['Order']['LineItems'][line]['PhysicalDetails']['PackQualifier'], - 'pack_value': orders['Orders']['Order']['LineItems'][line]['PhysicalDetails']['PackValue'], - 'pack_size': orders['Orders']['Order']['LineItems'][line]['PhysicalDetails']['PackSize'], - 'pack_uom': orders['Orders']['Order']['LineItems'][line]['PhysicalDetails']['PackUOM'], + 'pack_qualifier': orders['Orders']['Order']['LineItems'] + [line]['PhysicalDetails']['PackQualifier'], + 'pack_value': orders['Orders']['Order']['LineItems'][line] + ['PhysicalDetails']['PackValue'], + 'pack_size': orders['Orders']['Order']['LineItems'][line] + ['PhysicalDetails']['PackSize'], + 'pack_uom': orders['Orders']['Order']['LineItems'][line] + ['PhysicalDetails']['PackUOM'], 'packing_medium': '', 'packing_material': '', 'pack_weight': 0, 'pack_weight_uom': '', 'location_code_qualifier': '', 'location': '', - 'allow_chrg_indicator': orders['Orders']['Order']['LineItems'][line]['ChargesAllowances']['AllowChrgIndicator'], - 'allow_chrg_code': orders['Orders']['Order']['LineItems'][line]['ChargesAllowances']['AllowChrgCode'], + 'allow_chrg_indicator': orders['Orders']['Order']['LineItems'] + [line]['ChargesAllowances']['AllowChrgIndicator'], + 'allow_chrg_code': orders['Orders']['Order']['LineItems'] + [line]['ChargesAllowances']['AllowChrgCode'], 'allow_chrg_agency_code': '', 'allow_chrg_agency': '', - 'allow_chrg_amt': orders['Orders']['Order']['LineItems'][line]['ChargesAllowances']['AllowChrgAmt'], - 'allow_chrg_percent': orders['Orders']['Order']['LineItems'][line]['ChargesAllowances']['AllowChrgPercent'], + 'allow_chrg_amt': orders['Orders']['Order']['LineItems'][line] + ['ChargesAllowances']['AllowChrgAmt'], + 'allow_chrg_percent': orders['Orders']['Order']['LineItems'] + [line]['ChargesAllowances']['AllowChrgPercent'], 'percent_dollar_basis': 0, 'allow_chrg_rate': 0, - 'allow_chrg_handling_code': orders['Orders']['Order']['LineItems'][line]['ChargesAllowances']['AllowChrgHandlingCode'], + 'allow_chrg_handling_code': orders['Orders']['Order'] + ['LineItems'][line]['ChargesAllowances'] + ['AllowChrgHandlingCode'], 'allow_chrg_qty_uom': '', - # 'allow_chrg_handling_description':orders['Orders']['Order']['LineItems'][line]['ChargesAllowances']['allow_chrg_handling_description'], + # 'allow_chrg_handling_description':orders['Orders']['Order'] + # ['LineItems'][line]['ChargesAllowances']['allow_chrg_handling_description'], } record['order_line'].update(order_line_data) - print "Stop", record + print("Stop", record) sale_id = create_sale_order(sock, uid, record) - print "Successfully created ORDER IMPORTED!!!!", sale_id + print("Successfully created ORDER IMPORTED!!!!", sale_id) line_ids = create_so_lines(sock, uid, sale_id, record['order_line']) - print "Successfully created ORDER LINES IMPORTED!!!!", line_ids + print("Successfully created ORDER LINES IMPORTED!!!!", line_ids) return parse_csv @@ -521,7 +613,7 @@ def get_partner_info( try: # check for existing partner if not then create - print "LOOK FOR THIS", partner_contact['contact_name'] + print("LOOK FOR THIS", partner_contact['contact_name']) args = [('name', '=', partner_contact['contact_name'])] ids = sock.execute(DBNAME, uid, PWD, 'res.partner', 'search', args) if ids: @@ -560,7 +652,7 @@ def search_product(sock, uid, product_sku): product_id = ids[0] except Exception: product_id = 'no_exist' - print 'product does not exist' + print('product does not exist') return product_id @@ -589,8 +681,8 @@ def create_sale_order(sock, uid, order_data): if partner_info['id']: partner_invoice_id = partner_info['id'] partner_shipping_id = partner_info['id'] - print "datetime.now().strftime('%Y-%m-%d %H:%M:%S')", \ - datetime.now().strftime('%Y-%m-%d %H:%M:%S') + print("""datetime.now().strftime('%Y-%m-%d %H:%M:%S') + """, datetime.now().strftime('%Y-%m-%d %H:%M:%S')) sale_hash = { 'incoterm': 14, # hardcoded to "Delivery at Place" 'date_order': datetime.now().strftime('%Y-%m-%d %H:%M:%S'), @@ -658,9 +750,9 @@ def create_sale_order(sock, uid, order_data): order_data['allow_chrg_handling_description'], } - print "Attempting to create %s" % sale_hash + print("Attempting to create %s" % sale_hash) sale_id = sock.execute(DBNAME, uid, PWD, 'sale.order', 'create', sale_hash) - print "sale_idddddddddddddddddddddddd", sale_id + print("sale_idddddddddddddddddddddddd", sale_id) return sale_id @@ -788,6 +880,6 @@ def main(): if __name__ == '__main__': - print 'Process: EDI Orders Read - Starting' + print('Process: EDI Orders Read - Starting') main() - print 'Process: EDI Orders Read - Ending' + print('Process: EDI Orders Read - Ending') diff --git a/connector_spscommerce/edi_scripts/monitor.py b/connector_spscommerce/edi_scripts/monitor.py index 0a68031..9bd3b2e 100644 --- a/connector_spscommerce/edi_scripts/monitor.py +++ b/connector_spscommerce/edi_scripts/monitor.py @@ -24,7 +24,7 @@ count = 0 -print "Current date & time " + time.strftime("%c") +print("Current date & time " + time.strftime("%c")) def ftp_connect(HOST, UNAME, PW): @@ -83,10 +83,10 @@ def process_files(ftp, SOURCE_DIR, LOCAL_SOURCE_DIR, DEST_DIR, LOCAL_DEST_DIR): try: ftp.rename(SOURCE_DIR + filename, REMOTE_ARCHIVE + filename) - logging.info('Successfully transfered %s' % + logging.info('Successfully transfered %s' % filename + 'from remote host') - print 'Successful Transfer of: ' + filename + \ - ' from remote host' + print('''Successful Transfer of: + ''' + filename + ' from remote host') except Exception: logging.error( 'Failed to archive %s on remote server' % filename) @@ -99,9 +99,9 @@ def process_files(ftp, SOURCE_DIR, LOCAL_SOURCE_DIR, DEST_DIR, LOCAL_DEST_DIR): try: shutil.move(LOCAL_SOURCE_DIR + filename, ARCHIVE) - logging.info('Successfully transfered %s' % + logging.info('Successfully transfered %s' % filename + 'to remote host') - print 'Successful Transfer of: ' + filename + ' to remote host' + print('Successful Transfer of:' + filename + ' to remote host') except Exception: logging.error( 'Failed to archive %s on local server' % filename) @@ -130,7 +130,7 @@ def transfer_files(ftp, files, SOURCE_DIR, DEST_DIR, transfer_type): except Exception: res = False - logging.error('Error transferring ' + filename + + logging.error('Error transferring ' + filename + ' using transfer type: ' + transfer_type) return res @@ -143,7 +143,7 @@ def main(): if __name__ == '__main__': - print 'Starting' + print('Starting') main() - print 'Sleeping' + print('Sleeping') time.sleep(5) diff --git a/connector_spscommerce/edi_scripts/test.py b/connector_spscommerce/edi_scripts/test.py index 1271782..f6be1b9 100644 --- a/connector_spscommerce/edi_scripts/test.py +++ b/connector_spscommerce/edi_scripts/test.py @@ -18,7 +18,7 @@ def connect_oerp(): sock_common = xmlrpclib.ServerProxy(ERP_WWW + '/xmlrpc/common') uid = sock_common.login(DBNAME, USERNAME, PWD) - print "XMLRPC Connection: SUCCESS - SERVER HAS AUTHENTICATED A LOGIN" + print("XMLRPC Connection: SUCCESS - SERVER HAS AUTHENTICATED A LOGIN") return sock, uid @@ -67,10 +67,10 @@ def connect_oerp(): if count == 1 and row[0] != 'H': - print "INFO: FAILURE - YOUR FILE DOES NOT HAVE THE REQUIRED FIRST" \ - " ROW WITH COLUMN HEADINGS:" \ - " EDI Loop | Order line | Partner | PO # | Date/UPC |" \ - " code?/product desc | quantity | UOM Price *****" + print("""INFO: FAILURE - YOUR FILE DOES NOT HAVE THE REQUIRED + FIRST ROW WITH COLUMN HEADINGS: EDI Loop | Order line | Partner | + PO # | Date/UPC | code?/product desc | quantity | UOM + Price *****""") break if count > 2 and row[0] == '': @@ -80,7 +80,7 @@ def connect_oerp(): if row[0] == 'H': if record['sku']: - print record + print(record) edi_850.main(sock, uid, record, IN_PATH) record['sku'] = [] record['upc'] = [] @@ -136,10 +136,10 @@ def main(): sock, uid = connect_oerp() o_id = 33382 orders = sock.execute(DBNAME, uid, PWD, 'stock.picking', 'read', o_id) - print orders + print(orders) if __name__ == '__main__': - print 'Process: EDI Orders Read - Starting' + print('Process: EDI Orders Read - Starting') main() - print 'Process: EDI Orders Read - Ending' + print('Process: EDI Orders Read - Ending') diff --git a/connector_spscommerce/models/__init__.py b/connector_spscommerce/models/__init__.py index 6dc5d51..58be573 100644 --- a/connector_spscommerce/models/__init__.py +++ b/connector_spscommerce/models/__init__.py @@ -1,12 +1,11 @@ -# -*- coding: utf-8 -*- -# Copyright (c) Open Source Integrators # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from . import ( - edi_config, - sale, - purchase, - procurement, - stock, - stock_move, - account_invoice -) + +from . import edi_config +from . import res_company +from . import res_partner +from . import sale +from . import purchase +from . import stock +from . import stock_move +from . import account_invoice +from . import stock_picking diff --git a/connector_spscommerce/models/account_invoice.py b/connector_spscommerce/models/account_invoice.py index c8f1c17..d0e25b8 100644 --- a/connector_spscommerce/models/account_invoice.py +++ b/connector_spscommerce/models/account_invoice.py @@ -1,271 +1,529 @@ -# -*- coding: utf-8 -*- # Copyright (c) Open Source Integrators # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from datetime import datetime + import re import dicttoxml -from openerp.osv import fields, osv -import openerp.addons.decimal_precision as dp +from odoo import api, fields, models +from datetime import datetime +from odoo.addons import decimal_precision as dp +from odoo.tools import DEFAULT_SERVER_DATE_FORMAT as DS +from odoo.tools import DEFAULT_SERVER_DATETIME_FORMAT as DSD +from odoo.exceptions import UserError -class account_invoice_line(osv.osv): +class AccountInvoiceLine(models.Model): _inherit = "account.invoice.line" - _columns = { - 'asn_shipment': fields.char('ASN Shipment Number from Converted 856'), - 'po_number': fields.char('Line Item PO Number from Converted 856'), - 'buyer_part_number': fields.char(string='Buyer Part Number'), - 'edi_line_qty': fields.float('Original EDI Quantity', digits_compute=dp.get_precision('Product Unit of Measure')), - 'vendor_part_number': fields.char('VendorPartNumber'), - 'consumer_package_code': fields.char('ConsumerPackageCode'), - 'gtin': fields.char('GTIN'), - 'upc_case_code': fields.char('UPCCaseCode'), - 'purchase_price_basis': fields.char('PurchasePriceBasis'), - 'product_size_code': fields.char('ProductSizeCode'), - 'product_size_description': fields.char('ProductSizeDescription'), - 'product_color_code': fields.char('ProductColorCode'), - 'product_color_description': fields.char('ProductColorDescription'), - 'product_material_code': fields.char('ProductMaterialCode'), - 'product_material_description': fields.char('ProductMaterialDescription'), - 'department': fields.char('Department'), - 'classs': fields.char('Class'), - 'price_type_id_code': fields.char('PriceTypeIDCode'), - 'edi_line_num': fields.integer('EDI PO line number'), - 'multiple_price_quantity': fields.float('MultiplePriceQuantity'), - 'class_of_trade_code': fields.char('ClassOfTradeCode'), - 'item_description_type': fields.char('ItemDescriptionType'), - 'product_characteristic_code': fields.char('ProductCharacteristicCode'), - 'agency_qualifier_code': fields.char('AgencyQualifierCode'), - 'product_description_code': fields.char('ProductDescriptionCode'), - 'pack_qualifier': fields.char('PackQualifier'), - 'pack_value': fields.integer('PackValue'), - 'pack_size': fields.char('PackSize'), - 'pack_uom': fields.char('PackUOM'), - 'packing_medium': fields.char('PackingMedium'), - 'packing_material': fields.char('PackingMaterial'), - 'pack_weight': fields.float('PackWeight'), - 'pack_weight_uom': fields.char('PackWeightUOM'), - 'location_code_qualifier':fields.char('LocationCodeQualifier', help="Code identifying the structure or format of the related location number(s)."), - 'location':fields.char('Location', help="For CrossDock, it's the marked for location. For MultiStore[could also be DC] ship-to location."), - 'allow_chrg_indicator':fields.char('AllowChrgIndicator', help="Code which indicates an allowance or charge for the service specified."), - 'allow_chrg_code':fields.char('AllowChrgCode', help="Code describing the type of allowance or charge for the service specified."), - 'allow_chrg_agency_code':fields.char('AllowChrgAgencyCode', help="Code identifying the agency assigning the code values."), - 'allow_chrg_agency':fields.char('AllowChrgAgency', help="Agency maintained code identifying the service, promotion, allowance, or charge."), - 'allow_chrg_amt':fields.float('AllowChrgAmt', help="Amount of the allowance or charge."), - 'allow_chrg_percent':fields.float('AllowChrgPercent', help="Percentage of allowance or charge. Percentages should be represented as real numbers[0% through 100% should be normalized to 0.0 through 100.00].."), - 'percent_dollar_basis':fields.float('PercentDollarBasis', help="."), - 'allow_chrg_rate':fields.float('AllowChrgRate', help="Amount of the allowance or charge."), - 'allow_chrg_handling_code':fields.char('AllowChrgHandlingCode', help="Code indicating method of handling for an allowance or charge.."), - 'allow_chrg_qty_uom':fields.char('AllowChrgQtyUOM', help=""), - 'allow_chrg_handling_description':fields.char('AllowChrgHandlingDescription', help="Free-form textual description of the note."), - } + asn_shipment = fields.Char('ASN Shipment Number from Converted 856') + po_number = fields.Char('Line Item PO Number from Converted 856') + buyer_part_number = fields.Char('Buyer Part Number') + edi_line_qty = fields.Float( + 'Original EDI Quantity', + digits=dp.get_precision('Product Unit of Measure') + ) + vendor_part_number = fields.Char('Vendor Part Number') + consumer_package_code = fields.Char('Consumer Package Code') + gtin = fields.Char('GTIN') + upc_case_code = fields.Char('UPC Case Code') + purchase_price_basis = fields.Char('Purchase PriceBasis') + product_size_code = fields.Char('Product Size Code') + product_size_description = fields.Char('Product Size Description') + product_color_code = fields.Char('Product Color Code') + product_color_description = fields.Char('Product Color Description') + product_material_code = fields.Char('Product Material Code') + product_material_description = fields.Char('Product Material Description') + department = fields.Char('Department') + classs = fields.Char('Class') + price_type_id_code = fields.Char('PriceType ID Code') + edi_line_num = fields.Integer('EDI PO line number') + multiple_price_quantity = fields.Float('Multiple PriceQuantity') + class_of_trade_code = fields.Char('Class Of Trade Code') + item_description_type = fields.Char('Item Description Type') + product_characteristic_code = fields.Char('Product Characteristic Code') + agency_qualifier_code = fields.Char('Agency Qualifier Code') + product_description_code = fields.Char('Product Description Code') + pack_qualifier = fields.Char('Pack Qualifier') + pack_value = fields.Integer('Pack Value') + pack_size = fields.Char('Pack Size') + pack_uom = fields.Char('Pack UOM') + packing_medium = fields.Char('Packing Medium') + packing_material = fields.Char('Packing Material') + pack_weight = fields.Float('Pack Weight') + pack_weight_uom = fields.Char('Pack Weight UOM') + location_code_qualifier = fields.Char( + 'Location Code Qualifier', + help="Code identifying the structure or format of the related" + "location number(s)." + ) + location = fields.Char( + 'Location', + help="For CrossDock, it's the marked for location. For MultiStore" + "[could also be DC] ship-to location." + ) + allow_chrg_indicator = fields.Char( + 'Allow Chrg Indicator', + help="Code which indicates an allowance or charge for the" + "service specified." + ) + allow_chrg_code = fields.Char( + 'Allow Chrg Code', + help="Code describing the type of allowance or charge for the" + "service specified." + ) + allow_chrg_agency_code = fields.Char( + 'Allow Chrg Agency Code', + help="Code identifying the agency assigning the code values." + ) + allow_chrg_agency = fields.Char( + 'Allow Chrg Agency', + help="Agency maintained code identifying the service, promotion," + "allowance, or charge." + ) + allow_chrg_amt = fields.Float( + 'Allow Chrg Amt', + help="Amount of the allowance or charge." + ) + allow_chrg_percent = fields.Float( + 'Allow Chrg Percent', + help='''Percentage of allowance or charge. Percentages should be + represented as real numbers[0% through 100% should be normalized to + 0.0 through 100.00]..''' + ) + percent_dollar_basis = fields.Float( + 'Percent Dollar Basis', + ) + allow_chrg_rate = fields.Float( + 'Allow Chrg Rate', + help="Amount of the allowance or charge." + ) + allow_chrg_handling_code = fields.Char( + 'Allow Chrg Handling Code', + help="Code indicating method of handling for an allowance or charge.." + ) + allow_chrg_qty_uom = fields.Char('Allow Chrg Qty UOM') + allow_chrg_handling_description = fields.Char( + 'Allow Chrg Handling Description', + help="Free-form textual description of the note." + ) -class account_invoice(osv.osv): +class AccountInvoice(models.Model): _inherit = "account.invoice" - _columns = { - 'asn_shipment': fields.char('ASN Shipment Number from Converted 856'), - 'client_order_ref': fields.text('Customer PO #', help="Customer PO #"), - 'ship_to_code': fields.char('Ship To Warehouse', help="Trading Partner Ship to location code."), - 'supplier_code': fields.char('Supplier Code', help="This is the date from the 856."), - 'edi_yes': fields.boolean('From an EDI PO?', readonly=True, help="Is this order from an EDI purchase order, 850 EDI doc."), - '810_sent_timestamp': fields.datetime('810 Sent Date', help="The timestamp for when the 856 was sent."), - 'trading_partner_id': fields.many2one('edi.config', 'Trading Partner', help='EDI Configuration information for partner'), - 'invoice_check': fields.boolean('810 EDI Invoice Sent?', readonly=True, help="Is checked if EDI 810 invoice is sent."), - 'ship_not_before_date': fields.date('Estimated Shipping Date', help="This is the date from the 856."), - 'cancel_after_date': fields.date('Cancel if Shipped After This Date', help="Cancel if Shipped After This Date."), - 'sale_id': fields.many2one('sale.order', 'Sale Order', help='Sale Order from Whence this Invoice Was created'), - 'picking_id': fields.many2one('stock.picking', 'Picking ID', help='Stock Picking ID whence this invoice was created'), - 'scac_code': fields.char('SCAC Code', help="This is the shipping alpha code from your carrier."), - 'bol_num': fields.char('BoL Number', help="This is bill of lading number from your carrier/shipper."), - 'tracking_num': fields.char('Supplier Code', help="This is the tracking number from your carrier."), - 'sender_id':fields.char(string='EDI Sender Code',), - 'tset_purpose_code':fields.char('TsetPurposeCode', help="Code identifying purpose of the document.."), - 'purchase_order_type_code':fields.char('PurchaseOrderTypeCode', help="Code specifying the type of purchase order."), - 'po_type_description':fields.char('POTypeDescription', help="Free form text to describe the type of order."), - 'ship_complete_code':fields.char('ShipCompleteCode', help="Code to identify a specific requirement or agreement of sale. Should only be used to indicate if an item can be placed on backorder."), - 'department':fields.char('Department', help="Name or number identifying an area wherein merchandise is categorized within a store."), - 'division':fields.char('Division', help="Different entities belonging to the same parent company."), - 'promotion_deal_number':fields.char('PromotionDealNumber', help="Number uniquely identifying an agreement for a special offer or price."), - 'carrier_pro_number':fields.char('CarrierProNumber', help=""), - 'bill_of_lading_number':fields.char('BillOfLadingNumber', help=""), - 'terms_type':fields.char('TermsType', help="Code identifying type of payment terms."), - 'terms_basis_date_code':fields.char('TermsBasisDateCode', help="Code identifying the beginning of the terms period."), - 'terms_discount_percentage':fields.char('TermsDiscountPercentage', help="Terms discount percentage available to the purchaser"), - 'terms_discount_due_days':fields.char('TermsDiscountDueDays', help="Number of days by which payment or invoice must be received in order to receive the discount noted."), - 'terms_net_due_days':fields.char('TermsNetDueDays', help="Number of days until total invoice amount is due[discount not applicable."), - 'payment_method_code':fields.char('PaymentMethodCode', help="Indication of the instrument of payment."), - 'fob_pay_code':fields.char('FOBPayCode', help="Code identifying payment terms for transportation charges."), - 'fob_location_qualifier':fields.char('FOBLocationQualifier', help="Code identifying type of location at which ownership of goods is transferred."), - 'fob_location_description':fields.char('FOBLocationDescription', help="Free-form textual description of the location at which ownership of goods is transferred."), - 'fob_title_passage_code':fields.char('FOBTitlePassageCode', help="Code describing the location of ownership of the goods."), - 'fob_title_passage_location':fields.char('FOBTitlePassageLocation', help="Location of ownership of the goods."), - 'carrier_trans_method_code':fields.char('CarrierTransMethodCode', help="Code specifying the method or type of transportation for the shipment."), - 'carrier_alpha_code':fields.char('CarrierAlphaCode', help="Standard Carrier Alpha Code[SCAC] - "), - 'carrier_routing':fields.char('CarrierRouting', help="Free-form description of the routing/requested routing for shipment or the originating carrier's identity."), - 'routing_sequence_code':fields.char('RoutingSequenceCode', help=""), - 'service_level_code':fields.char('ServiceLevelCode', help="Code indicating the level of transportation service or the billing service offered by the transportation carrier."), - 'reference_qual':fields.char('ReferenceQual', help="Code specifying the type of data in the ReferenceID/ReferenceDescription."), - 'reference_id':fields.char('ReferenceID', help="Code specifying the type of data in the ReferenceID/ReferenceDescription."), - 'ref_description':fields.char('Description', help="Free-form textual description to clarify the related data elements and their content."), - 'note_code':fields.char('NoteCode', help="Code specifying the type of note."), - 'note_information_field':fields.char('NoteInformationField', help="Free-form textual description of the note."), - 'allow_chrg_indicator':fields.char('AllowChrgIndicator', help="Code which indicates an allowance or charge for the service specified."), - 'allow_chrg_code':fields.char('AllowChrgCode', help="Code describing the type of allowance or charge for the service specified."), - 'allow_chrg_agency_code':fields.char('AllowChrgAgencyCode', help="Code identifying the agency assigning the code values."), - 'allow_chrg_agency':fields.char('AllowChrgAgency', help="Agency maintained code identifying the service, promotion, allowance, or charge."), - 'allow_chrg_amt':fields.float('AllowChrgAmt', help="Amount of the allowance or charge."), - 'allow_chrg_percent_qual':fields.char('AllowChrgPercentQual', help="Code indicating on what basis an allowance or charge percent is calculated.."), - 'allow_chrg_percent':fields.float('AllowChrgPercent', help="Percentage of allowance or charge. Percentages should be represented as real numbers[0% through 100% should be normalized to 0.0 through 100.00].."), - 'allow_chrg_handling_code':fields.char('AllowChrgHandlingCode', help="Code indicating method of handling for an allowance or charge.."), - 'reference_identification':fields.char('ReferenceIdentification', help=""), - 'allow_chrg_handling_description':fields.char('AllowChrgHandlingDescription', help="Free-form textual description of the note."), - } - - def create_text_810(self, cr, uid, invoice_ids, context=None): - + asn_shipment = fields.Char('ASN Shipment Number from Converted 856') + client_order_ref = fields.Text( + 'Customer PO #', + help="Customer PO #" + ) + ship_to_code = fields.Char( + 'Ship To Warehouse', + help="Trading Partner Ship to location code." + ) + supplier_code = fields.Char( + 'Supplier Code', + help="This is the date from the 856." + ) + edi_yes = fields.Boolean( + 'From an EDI PO?', + # readonly=True, + help="Is this order from an EDI purchase order, 850 EDI doc." + ) + sent_timestamp = fields.Datetime( + '810 Sent Date', + help="The timestamp for when the 856 was sent." + ) + trading_partner_id = fields.Many2one( + 'edi.config', + 'Trading Partner', + help='EDI Configuration information for partner' + ) + invoice_check = fields.Boolean( + '810 EDI Invoice Sent?', + readonly=True, + help="Is checked if EDI 810 invoice is sent." + ) + ship_not_before_date = fields.Date( + 'Estimated Shipping Date', + help="This is the date from the 856." + ) + cancel_after_date = fields.Date( + 'Cancel if Shipped After This Date', + help="Cancel if Shipped After This Date." + ) + sale_id = fields.Many2one( + 'sale.order', + 'Sale Order', + help='Sale Order from Whence this Invoice Was created' + ) + picking_id = fields.Many2one( + 'stock.picking', + 'Picking ID', + help='Stock Picking ID whence this invoice was created' + ) + scac_code = fields.Char( + 'SCAC Code', + help="This is the shipping alpha code from your carrier." + ) + bol_num = fields.Char( + 'BoL Number', + help="This is bill of lading number from your carrier/shipper." + ) + tracking_num = fields.Char( + 'Supplier Code', + help="This is the tracking number from your carrier." + ) + sender_id = fields.Char('EDI Sender Code') + tset_purpose_code = fields.Char( + 'TsetPurposeCode', + help="Code identifying purpose of the document.." + ) + purchase_order_type_code = fields.Char( + 'Purchase Order Type Code', + help="Code specifying the type of purchase order." + ) + po_type_description = fields.Char( + 'PO Type Description', + help="Free form text to describe the type of order." + ) + ship_complete_code = fields.Char( + 'Ship Complete Code', + help='''Code to identify a specific requirement or agreement of sale. + Should only be used to indicate if an item can be + placed on backorder.''' + ) + department = fields.Char( + 'Department', + help="Name or number identifying an area wherein merchandise is" + "categorized within a store." + ) + division = fields.Char( + 'Division', + help="Different entities belonging to the same parent company." + ) + promotion_deal_number = fields.Char( + 'Promotion Deal Number', + help="Number uniquely identifying an agreement for a" + "special offer or price." + ) + carrier_pro_number = fields.Char('Carrier Pro Number') + bill_of_lading_number = fields.Char('Bill Of Lading Number') + terms_type = fields.Char( + 'Terms Type', + help="Code identifying type of payment terms." + ) + terms_basis_date_code = fields.Char( + 'Terms Basis Date Code', + help="Code identifying the beginning of the terms period." + ) + terms_discount_percentage = fields.Char( + 'Terms Discount Percentage', + help="Terms discount percentage available to the purchaser" + ) + terms_discount_due_days = fields.Char( + 'Terms Discount Due Days', + help="Number of days by which payment or invoice must be received in" + "order to receive the discount noted." + ) + terms_net_due_days = fields.Char( + 'Terms Net Due Days', + help="Number of days until total invoice amount is due[discount" + "not applicable." + ) + payment_method_code = fields.Char( + 'Payment Method Code', + help="Indication of the instrument of payment." + ) + fob_pay_code = fields.Char( + 'FOB Pay Code', + help="Code identifying payment terms for transportation Charges." + ) + fob_location_qualifier = fields.Char( + 'FOB Location Qualifier', + help="Code identifying type of location at which ownership of" + "goods is transferred." + ) + fob_location_description = fields.Char( + 'FOB Location Description', + help="Free-form textual description of the location at which" + "ownership of goods is transferred." + ) + fob_title_passage_code = fields.Char( + 'FOB Title Passage Code', + help="Code describing the location of ownership of the goods." + ) + fob_title_passage_location = fields.Char( + 'FOB Title Passage Location', + help="Location of ownership of the goods." + ) + carrier_trans_method_code = fields.Char( + 'Carrier Trans Method Code', + help="Code specifying the method or type of transportation" + "for the shipment." + ) + carrier_alpha_code = fields.Char( + 'CarrierAlphaCode', + help="Standard Carrier Alpha Code[SCAC] - " + ) + carrier_routing = fields.Char( + 'CarrierRouting', + help="Free-form description of the routing/requested routing for" + "shipment or the originating carrier's identity." + ) + routing_sequence_code = fields.Char('Routing Sequence Code') + service_level_code = fields.Char( + 'Service Level Code', + help="Code indicating the level of transportation service or the" + "billing service offered by the transportation carrier." + ) + reference_qual = fields.Char( + 'Reference Qual', + help="Code specifying the type of data in the" + "ReferenceID/ReferenceDescription." + ) + reference_id = fields.Char( + 'Reference ID', + help="Code specifying the type of data in the" + "ReferenceID/ReferenceDescription." + ) + ref_description = fields.Char( + 'Description', + help="Free-form textual description to clarify the related data" + "elements and their content." + ) + note_code = fields.Char( + 'NoteCode', + help="Code specifying the type of note." + ) + note_information_field = fields.Char( + 'Note Information Field', + help="Free-form textual description of the note." + ) + allow_chrg_indicator = fields.Char( + 'Allow Chrg Indicator', + help="Code which indicates an allowance or Charge for the" + "service specified." + ) + allow_chrg_code = fields.Char( + 'Allow Chrg Code', + help="Code describing the type of allowance or Charge for" + "the service specified." + ) + allow_chrg_agency_code = fields.Char( + 'Allow Chrg Agency Code', + help="Code identifying the agency assigning the code values." + ) + allow_chrg_agency = fields.Char( + 'Allow Chrg Agency', + help="Agency maintained code identifying the service, promotion," + "allowance, or charge." + ) + allow_chrg_amt = fields.Float( + 'Allow Chrg Amt', + help="Amount of the allowance or Charge." + ) + allow_chrg_percent_qual = fields.Char( + 'Allow Chrg Percent Qual', + help="Code indicating on what basis an allowance or charge" + "percent is calculated.." + ) + allow_chrg_percent = fields.Float( + 'Allow Chrg Percent', + help='''Percentage of allowance or charge. Percentages should be + represented as real numbers[0% through 100% should be + normalized to 0.0 through 100.00]..''' + ) + allow_chrg_handling_code = fields.Char( + 'Allow Chrg Handling Code', + help="Code indicating method of handling for an allowance or charge.." + ) + reference_identification = fields.Char('Reference Identification') + allow_chrg_handling_description = fields.Char( + 'Allow Chrg Handling Description', + help="Free-form textual description of the note." + ) + + @api.multi + def create_text_810(self): + today = str(datetime.now().strftime('%Y%m%d')) or '' processed = 0 num = '' + # for each invoice - invoices_list = {'Invoices':[]} - for invoice in self.browse(cr, uid, invoice_ids, context=context): - num += invoice.number + invoices_list = {'Invoices': []} + for invoice in self: + num += invoice.number or '1' + # skip non customer invoice records - if invoice.type not in ('out_invoice') or invoice.state in ('draft', 'cancel', 'paid'): - continue - - # Grab the vendor_id and trading_partner_id for EDI in the edi_config. - trading_partner_code = invoice.trading_partner_id.partner_header_string - vendor_code = invoice.trading_partner_id.vendor_header_string - - date_format = "%Y-%m-%d" +# if invoice.type not in ('out_invoice' +# ) or invoice.state in ('draft', 'cancel', 'paid'): +# continue + + # Grab the vendor_id and trading_partner_id for EDI + # in the edi_config. + trading_partner_code = \ + invoice.trading_partner_id.partner_header_string or 'Test' + vendor_code =\ + invoice.trading_partner_id.vendor_header_string or 'Test' + + # date_format = "%Y-%m-%d" # invoice date inv_date = invoice.date_invoice - dateinv_object = datetime.strptime(inv_date, date_format) + dateinv_object = datetime.strptime(inv_date, DS) inv_date = dateinv_object.date() inv_date = str(inv_date) - + # purchase date - po_date = invoice.purchase_id and invoice.purchase_id.date_order or '' + po_date = \ + invoice.purchase_id and invoice.purchase_id.date_order or '' if po_date: - datepo_object = datetime.strptime(po_date, date_format) + datepo_object = datetime.strptime(po_date, DS) po_date = datepo_object.date() po_date = str(po_date) - + # ship date - ship_date = invoice.picking_id and invoice.picking_id.date_done or '' + ship_date = \ + invoice.picking_id and invoice.picking_id.date_done or '' if ship_date: - ship_object = datetime.strptime(ship_date, date_format) + ship_object = datetime.strptime(ship_date, DS) ship_date = ship_object.date() ship_date = str(ship_date) - + # initialize - invoice_dict = {'Invoice':{}} + invoice_dict = {'Invoice': {}} # 1. Header Line - one line for the order meta_dict = {'Meta': {'Version': '1.0'}} header_dict = {'Header': {'InvoiceHeader': { 'TradingPartnerId': trading_partner_code, 'InvoiceNumber': str(invoice.number), - 'InvoiceDate':inv_date, - 'PurchaseOrderDate':po_date, - 'PurchaseOrderNumber':invoice.purchase_id and invoice.purchase_id.number or '', - 'ReleaseNumber':str(invoice.number), - 'InvoiceTypeCode':'U5', - 'BuyersCurrency':invoice.currency_id and invoice.currency_id.name or '', - 'Department':invoice.department or '', - 'Vendor':vendor_code, - 'PromotionDealNumber':invoice.promotion_deal_number or '', - 'CarrierProNumber':invoice.carrier_pro_number or '', - 'BillOfLadingNumber':invoice.bill_of_lading_number, - 'ShipDate':ship_date, - 'CustomerOrderNumber':invoice.sale_id and invoice.sale_id.name or '', + 'InvoiceDate': inv_date, + 'PurchaseOrderDate': po_date, + 'PurchaseOrderNumber': + invoice.purchase_id and invoice.purchase_id.number or '', + 'ReleaseNumber': str(invoice.number), + 'InvoiceTypeCode': 'U5', + 'BuyersCurrency': + invoice.currency_id and invoice.currency_id.name or '', + 'Department': invoice.department or '', + 'Vendor': vendor_code, + 'PromotionDealNumber': invoice.promotion_deal_number or '', + 'CarrierProNumber': invoice.carrier_pro_number or '', + 'BillOfLadingNumber': invoice.bill_of_lading_number, + 'ShipDate': ship_date, + 'CustomerOrderNumber': + invoice.sale_id and invoice.sale_id.name or '', }, 'PaymentTerms': { - 'TermsType':invoice.terms_type or '', - 'TermsBasisDateCode':invoice.terms_basis_date_code or '', - 'TermsDiscountPercentage':invoice.terms_discount_percentage or '', - 'TermsDiscountDate':inv_date, - 'TermsDiscountDueDays':invoice.terms_discount_due_days or '', - 'TermsNetDueDate':inv_date, - 'TermsNetDueDays':invoice.terms_net_due_days or '', - 'TermsDiscountAmount':0, - 'TermsDescription':invoice.payment_term_id.note or '', + 'TermsType': invoice.terms_type or '', + 'TermsBasisDateCode': invoice.terms_basis_date_code or '', + 'TermsDiscountPercentage': + invoice.terms_discount_percentage or '', + 'TermsDiscountDate': inv_date, + 'TermsDiscountDueDays': + invoice.terms_discount_due_days or '', + 'TermsNetDueDate': inv_date, + 'TermsNetDueDays': invoice.terms_net_due_days or '', + 'TermsDiscountAmount': 0, + 'TermsDescription': invoice.payment_term_id.note or '', }, - 'Date': { - 'DateTimeQualifier1':'196', - 'Date1':inv_date, - 'Time1':'', + 'Date': { + 'DateTimeQualifier1': '196', + 'Date1': inv_date, + 'Time1': '', }, - 'Contact':{ - 'ContactTypeCode':'BD', - 'ContactName':invoice.picking_id and invoice.picking_id.partner_id and invoice.picking_id.partner_id.name or '', - 'PrimaryPhone':invoice.picking_id and invoice.picking_id.partner_id and invoice.picking_id.partner_id.phone or '', - 'PrimaryFax':invoice.picking_id and invoice.picking_id.partner_id and invoice.picking_id.partner_id.fax or '', - 'PrimaryEmail':invoice.picking_id and invoice.picking_id.partner_id and invoice.picking_id.partner_id.email or '', + 'Contact': { + 'ContactTypeCode': 'BD', + 'ContactName': + invoice.picking_id and invoice.picking_id.partner_id and + invoice.picking_id.partner_id.name or '', + 'PrimaryPhone': + invoice.picking_id and invoice.picking_id.partner_id and + invoice.picking_id.partner_id.phone or '', + 'PrimaryFax': + invoice.picking_id and invoice.picking_id.partner_id and + invoice.picking_id.partner_id.fax or '', + 'PrimaryEmail': + invoice.picking_id and invoice.picking_id.partner_id and + invoice.picking_id.partner_id.email or '', }, - 'Address':{ - 'AddressTypeCode':'Z7', - 'LocationCodeQualifier':'92', - 'AddressLocationNumber':'11111', - 'AddressName':invoice.partner_id.name, - 'Address1':invoice.partner_id.street, - 'Address2':invoice.partner_id.street2, - 'City':invoice.partner_id.city, - 'State':invoice.partner_id.state_id.name, - 'PostalCode':invoice.partner_id.zip, - 'Country':invoice.partner_id.country_id.name, - 'Contact':{ - 'ContactTypeCode':'BD', - 'ContactName':invoice.picking_id and invoice.picking_id.partner_id and invoice.partner_id.name or '', - 'PrimaryPhone':invoice.picking_id and invoice.picking_id.partner_id and invoice.partner_id.phone or '', - 'PrimaryFax':invoice.picking_id and invoice.picking_id.partner_id and invoice.partner_id.fax or '', - 'PrimaryEmail':invoice.picking_id and invoice.picking_id.partner_id and invoice.email or '', + 'Address': { + 'AddressTypeCode': 'Z7', + 'LocationCodeQualifier': '92', + 'AddressLocationNumber': '11111', + 'AddressName': invoice.partner_id.name, + 'Address1': invoice.partner_id.street, + 'Address2': invoice.partner_id.street2, + 'City': invoice.partner_id.city, + 'State': invoice.partner_id.state_id.name, + 'PostalCode': invoice.partner_id.zip, + 'Country': invoice.partner_id.country_id.name, + 'Contact': { + 'ContactTypeCode': 'BD', + 'ContactName': invoice.picking_id and + invoice.picking_id.partner_id and + invoice.partner_id.name or '', + 'PrimaryPhone': invoice.picking_id and + invoice.picking_id.partner_id and + invoice.partner_id.phone or '', + 'PrimaryFax': invoice.picking_id and + invoice.picking_id.partner_id and + invoice.partner_id.fax or '', + 'PrimaryEmail': invoice.picking_id and + invoice.picking_id.partner_id and invoice.email or '', }, }, - 'Reference':{ - 'ReferenceQual':invoice.reference_qual or '', - 'ReferenceID':invoice.reference_id or '', - 'Description':invoice.ref_description or '', + 'Reference': { + 'ReferenceQual': invoice.reference_qual or '', + 'ReferenceID': invoice.reference_id or '', + 'Description': invoice.ref_description or '', }, - 'Notes':{ - 'NoteCode':invoice.note_code or '', - 'NoteInformationField':invoice.note_information_field or '', + 'Notes': { + 'NoteCode': invoice.note_code or '', + 'NoteInformationField': + invoice.note_information_field or '', }, - 'Tax':{ - 'TaxTypeCode':'S', - 'TaxAmount':'1845.08', - 'TaxPercent':'8.50', - 'JurisdictionQual':'CC', - 'JurisdictionCode':'07034', - 'TaxExemptCode':'2', - 'TaxID':'99990000', + 'Tax': { + 'TaxTypeCode': 'S', + 'TaxAmount': '1845.08', + 'TaxPercent': '8.50', + 'JurisdictionQual': 'CC', + 'JurisdictionCode': '07034', + 'TaxExemptCode': '2', + 'TaxID': '99990000', }, - 'ChargesAllowances':{ - 'AllowChrgIndicator':invoice.allow_chrg_indicator or '', - 'AllowChrgCode':invoice.allow_chrg_code or '', - 'AllowChrgAmt':invoice.allow_chrg_amt or 0, - 'AllowChrgPercentQual':invoice.allow_chrg_percent_qual or '', - 'AllowChrgPercent':invoice.allow_chrg_percent or 0, - 'AllowChrgHandlingCode':invoice.allow_chrg_handling_code or '', - 'AllowChrgHandlingDescription':invoice.allow_chrg_handling_description or '', + 'ChargesAllowances': { + 'AllowChrgIndicator': invoice.allow_chrg_indicator or '', + 'AllowChrgCode': invoice.allow_chrg_code or '', + 'AllowChrgAmt': invoice.allow_chrg_amt or 0, + 'AllowChrgPercentQual': + invoice.allow_chrg_percent_qual or '', + 'AllowChrgPercent': invoice.allow_chrg_percent or 0, + 'AllowChrgHandlingCode': + invoice.allow_chrg_handling_code or '', + 'AllowChrgHandlingDescription': + invoice.allow_chrg_handling_description or '', }, - 'FOBRelatedInstruction':{ - 'FOBPayCode':invoice.fob_pay_code or '', - 'FOBLocationQualifier': invoice.fob_location_qualifier or '', - 'FOBLocationDescription':invoice.fob_location_description or '', - 'FOBTitlePassageCode': invoice.fob_title_passage_code or '', - 'FOBTitlePassageLocation':invoice.fob_title_passage_location or '', + 'FOBRelatedInstruction': { + 'FOBPayCode': invoice.fob_pay_code or '', + 'FOBLocationQualifier': + invoice.fob_location_qualifier or '', + 'FOBLocationDescription': + invoice.fob_location_description or '', + 'FOBTitlePassageCode': + invoice.fob_title_passage_code or '', + 'FOBTitlePassageLocation': + invoice.fob_title_passage_location or '', }, - 'CarrierInformation':{ - 'CarrierTransMethodCode':invoice.carrier_trans_method_code or '', - 'CarrierAlphaCode':invoice.carrier_alpha_code or '', - 'CarrierRouting':invoice.carrier_routing or '', - 'CarrierEquipmentNumber':invoice.routing_sequence_code or '', + 'CarrierInformation': { + 'CarrierTransMethodCode': + invoice.carrier_trans_method_code or '', + 'CarrierAlphaCode': invoice.carrier_alpha_code or '', + 'CarrierRouting': invoice.carrier_routing or '', + 'CarrierEquipmentNumber': + invoice.routing_sequence_code or '', }, - 'ServiceLevelCodes':{ - 'ServiceLevelCode':invoice.service_level_code or '', + 'ServiceLevelCodes': { + 'ServiceLevelCode': invoice.service_level_code or '', }, } } @@ -280,116 +538,134 @@ def create_text_810(self, cr, uid, invoice_ids, context=None): for line in invoice.invoice_line_ids: total_lines += 1 total_qty += line.quantity - total_weight += (line.product_id.weight * line.quantity) - lineitem_dict = {'LineItem':{}} + total_weight += (line.product_id.weight * line.quantity) + lineitem_dict = {'LineItem': {}} invoiceline_dict = {'InvoiceLine': { - 'LineSequenceNumber':int(line.id), - 'BuyerPartNumber':line.buyer_part_number or '', - 'VendorPartNumber':line.product_id.default_code or '', - 'ConsumerPackageCode':'093597609541', - 'EAN':line.product_id.barcode, - 'GTIN':line.gtin or '', - 'UPCCaseCode':line.upc_case_code or '', - 'NatlDrugCode':'51456-299', - 'InternationalStandardBookNumber':'999-0-555-22222-0', - 'ProductID':{ - 'PartNumberQual':'IS', - 'PartNumber':line.product_id.default_code, + 'LineSequenceNumber': int(line.id), + 'BuyerPartNumber': line.buyer_part_number or '', + 'VendorPartNumber': line.product_id.default_code or '', + 'ConsumerPackageCode': '093597609541', + 'EAN': line.product_id.barcode, + 'GTIN': line.gtin or '', + 'UPCCaseCode': line.upc_case_code or '', + 'NatlDrugCode': '51456-299', + 'InternationalStandardBookNumber': '999-0-555-22222-0', + 'ProductID': { + 'PartNumberQual': 'IS', + 'PartNumber': line.product_id.default_code, }, - 'PurchasePrice':line.price_unit, - 'ShipQty':line.quantity, - 'ShipQtyUOM':'P4', - 'ProductSizeCode':line.product_size_code or '', - 'ProductSizeDescription':line.product_size_description or '', - 'ProductColorCode':line.product_color_code or '', - 'ProductColorDescription':line.product_color_description or '', - 'ProductMaterialDescription':line.product_material_description or '', - 'NRFStandardColorAndSize':{ - 'NRFColorCode':'600', - 'NRFSizeCode':'42-10651', + 'PurchasePrice': line.price_unit, + 'ShipQty': line.quantity, + 'ShipQtyUOM': 'P4', + 'ProductSizeCode': line.product_size_code or '', + 'ProductSizeDescription': + line.product_size_description or '', + 'ProductColorCode': line.product_color_code or '', + 'ProductColorDescription': + line.product_color_description or '', + 'ProductMaterialDescription': + line.product_material_description or '', + 'NRFStandardColorAndSize': { + 'NRFColorCode': '600', + 'NRFSizeCode': '42-10651', } }, - 'ProductOrItemDescription':{ - 'ItemDescriptionType':line.item_description_type or '', - 'AgencyQualifierCode':line.agency_qualifier_code or '', - 'ProductDescriptionCode':line.product_id.default_code, - 'ProductDescription':line.name, + 'ProductOrItemDescription': { + 'ItemDescriptionType': + line.item_description_type or '', + 'AgencyQualifierCode': + line.agency_qualifier_code or '', + 'ProductDescriptionCode': + line.product_id.default_code, + 'ProductDescription': line.name, }, - 'PhysicalDetails':{ + 'PhysicalDetails': { 'PackQualifier': line.pack_qualifier or '', - 'PackValue':line.pack_value or 0, - 'PackSize':line.pack_size or '', - 'PackUOM':line.pack_uom or '', + 'PackValue': line.pack_value or 0, + 'PackSize': line.pack_size or '', + 'PackUOM': line.pack_uom or '', }, - 'Tax':{ - 'TaxTypeCode':'S', - 'TaxAmount':'1845.08', - 'TaxPercent':'8.50', - 'JurisdictionQual':'CC', - 'JurisdictionCode':'07034', - 'TaxExemptCode':'2', - 'TaxID':'99990000', + 'Tax': { + 'TaxTypeCode': 'S', + 'TaxAmount': '1845.08', + 'TaxPercent': '8.50', + 'JurisdictionQual': 'CC', + 'JurisdictionCode': '07034', + 'TaxExemptCode': '2', + 'TaxID': '99990000', }, - 'ChargesAllowances':{ - 'AllowChrgIndicator':line.allow_chrg_indicator or '', - 'AllowChrgCode':line.allow_chrg_code or '', - 'AllowChrgAmt':line.allow_chrg_amt or 0, - 'AllowChrgPercentQual':'', - 'AllowChrgPercent':line.allow_chrg_percent or 0, - 'AllowChrgHandlingCode':line.allow_chrg_handling_code or '', - 'AllowChrgHandlingDescription':line.allow_chrg_handling_description or '', + 'ChargesAllowances': { + 'AllowChrgIndicator': line.allow_chrg_indicator or '', + 'AllowChrgCode': line.allow_chrg_code or '', + 'AllowChrgAmt': line.allow_chrg_amt or 0, + 'AllowChrgPercentQual': '', + 'AllowChrgPercent': line.allow_chrg_percent or 0, + 'AllowChrgHandlingCode': + line.allow_chrg_handling_code or '', + 'AllowChrgHandlingDescription': + line.allow_chrg_handling_description or '', }, } lineitem_dict.get('LineItem').update(invoiceline_dict) lineitems_list.get('LineItems').append(lineitem_dict) - invoice_dict.get('Invoice').update(lineitems_list) - + # Summary summary_dict = {'Summary': { - 'TotalAmount':invoice.amount_total or '', - 'TotalNetSalesAmount':invoice.amount_untaxed, - 'TotalTermsDiscountAmount':0, - 'TotalQtyInvoiced':total_qty, - 'TotalWeight':total_weight, - 'TotalLineItemNumber':total_lines, - 'InvoiceAmtDueByTermsDate':0, - 'TotalQtyInvoicedUOM':'P3', - 'TotalWeightUOM':'HD', + 'TotalAmount': invoice.amount_total or '', + 'TotalNetSalesAmount': invoice.amount_untaxed, + 'TotalTermsDiscountAmount': 0, + 'TotalQtyInvoiced': total_qty, + 'TotalWeight': total_weight, + 'TotalLineItemNumber': total_lines, + 'InvoiceAmtDueByTermsDate': 0, + 'TotalQtyInvoicedUOM': 'P3', + 'TotalWeightUOM': 'HD', } } invoice_dict.get('Invoice').update(summary_dict) # update invoice - today = datetime.now().strftime("%Y-%m-%d %H:%M:%S") - invoice.write({'810_sent_timestamp':today}) + today = datetime.now().strftime(DSD) + invoice.write({'sent_timestamp': today}) invoices_list.get('Invoices').append(invoice_dict) processed += 1 + # Convert dictionary to xml xml = dicttoxml.dicttoxml(invoices_list, attr_type=False, root=False) + xml = xml.decode("utf-8") xml = xml.replace('', '').replace('', '') - xml = xml.replace('', '').replace('', '') + xml = xml.replace('', '').\ + replace('', + '' + + '') + print("***** SUCCESSFULLY STORED *****") # Write ASN doc to text file - num = re.findall('\d+', num)[0] + num = re.findall('\d+', num) filename = '810_' + today + '%s.xml' % num filename.replace('/', '_') + if not invoice.trading_partner_id.out_path: + raise UserError('Add out path in Trading Partner') fd = open(invoice.trading_partner_id.out_path + filename, 'w') fd.write(xml) fd.close() return processed - - def _create_810_wrapper(self, cr, uid, context=None): - # search for invoices that are edi_yes = True and 810_sent_timestamp = False. - eligible_invoices = self.search(cr, uid, [('edi_yes', '=', True), ('810_sent_timestamp', '=', False)], context=context) - return eligible_invoices and self.create_text_810(cr, uid, eligible_invoices, context=context) or False - # done as a server action - def action_create_text_810(self, cr, uid, ids, context=None): + @api.model + def _create_810_wrapper(self): + # search for invoices that are edi_yes = True + # and sent_timestamp = False. + eligible_invoices = self.search([('edi_yes', '=', True), + ('sent_timestamp', '=', False)]) + return eligible_invoices and\ + eligible_invoices.create_text_810() or False + + # done as a server action + @api.multi + def action_create_text_810(self): """ Creates a new 810 and puts it into the outbox """ - if context is None: - context = {} # number of orders to process - toprocess = len(ids) + toprocess = len(self.ids) # process orders to write 810 - processed = self.create_text_810(cr, uid, ids, context=context) - return (toprocess - processed) + processed = self.create_text_810() + return (toprocess - processed) diff --git a/connector_spscommerce/models/edi_config.py b/connector_spscommerce/models/edi_config.py index e67d6bb..de7c484 100644 --- a/connector_spscommerce/models/edi_config.py +++ b/connector_spscommerce/models/edi_config.py @@ -1,81 +1,56 @@ -# -*- coding: utf-8 -*- # Copyright (c) Open Source Integrators # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from openerp.osv import fields, orm, osv +from odoo import api, fields, models -class edi_config(osv.osv): +class EdiConfig(models.Model): _name = "edi.config" _description = "EDI Configuration Systems" - _columns = { - 'edi_company_id': fields.many2one('res.company', - string='EDI Company', - help="The Main EDI Company.", - required=False, ondelete='cascade'), - 'route_id': fields.many2one('stock.location.route', - string='Stock Transfer Route', - help="""Select Dropshipping if applicable - or another route. This will be the route - used in sale order lines."""), - 'vendor_header_string': fields.char(string='EDI Vendor ID',), - 'partner_header_string': fields.char(string='Partner Header ID',), - 'billto_header_string': fields.char(string='Bill to Header ID',), - 'salesperson': fields.many2one('res.partner', string='Sales Person', - help="The Salesperson for EDI Trading" - " Partner.", - required=False, ondelete='cascade'), - 'in_path': fields.char(string='EDI In Path',), - 'out_path': fields.char(string='EDI Out Path',), - 'log_path': fields.char(string='EDI Logs Path',), - 'archive_path': fields.char(string='EDI Archive Path',), - 'trading_partner_id': fields.many2one('res.partner', - string='Trading Partner', - help="The trading partner for" - " EDI.", - required=False, - ondelete='cascade'), - 'is_thirdparty': fields.boolean('Ship Directly to Customer'), - 'is_sku': fields.boolean('Use SKU instead of UPC'), - 'auto_workflow': fields.many2one('sale.workflow.process', - string='Automatic Workflow', - ondelete='restrict'), - 'ack_855': fields.boolean('EDI 855 Ack'), - 'ack_997': fields.boolean('EDI 997 Ack'), - } - - def name_get(self, cr, uid, ids, context=None): - if context is None: - context = {} + edi_company_id = fields.Many2one( + 'res.company', + string='EDI Company', + help="The Main EDI Company.", + ondelete='cascade' + ) + route_id = fields.Many2one( + 'stock.location.route', + string='Stock Transfer Route', + help="""Select Dropshipping if applicable or another route. This will + be the route used in sale order lines.""" + ) + vendor_header_string = fields.Char('EDI Vendor ID') + partner_header_string = fields.Char('Partner Header ID') + billto_header_string = fields.Char('Bill to Header ID') + salesperson = fields.Many2one( + 'res.partner', + string='Sales Person', + help="The Salesperson for EDI Trading Partner.", + ondelete='cascade' + ) + in_path = fields.Char('EDI In Path') + out_path = fields.Char('EDI Out Path') + log_path = fields.Char('EDI Logs Path') + archive_path = fields.Char('EDI Archive Path') + trading_partner_id = fields.Many2one( + 'res.partner', + string='Trading Partner', + help="The trading partner for EDI.", + ondelete='cascade' + ) + is_thirdparty = fields.Boolean('Ship Directly to Customer') + is_sku = fields.Boolean('Use SKU instead of UPC') + auto_workflow = fields.Many2one( + 'sale.workflow.process', + string='Automatic Workflow', + ondelete='restrict' + ) + ack_855 = fields.Boolean('EDI 855 Ack') + ack_997 = fields.Boolean('EDI 997 Ack') + + @api.multi + def name_get(self): res = [] - for record in self.browse(cr, uid, ids, context=context): - res.append((record['id'], record.trading_partner_id.name)) + for record in self: + res.append((record['id'], record.trading_partner_id.name)) return res - - -class company_config(orm.Model): - _inherit = 'res.company' - - _columns = { - 'header_string': fields.char(string='Company Header String',), - 'trading_partner_id': fields.many2many('edi.config', - string='EDI Trading Partner', - required=False, - ondelete='cascade'), - } - - -class res_partner(orm.Model): - _inherit = 'res.partner' - - _columns = { - 'trading_partner_res': fields.one2many('edi.config', - 'trading_partner_id', - required=False, - ondelete='cascade', - help=''), - 'edi_ids': fields.one2many('edi.config', 'edi_company_id', - 'EDI Company ID'), - 'ship_to_code': fields.char(string='Ship to Code',), - 'sender_id': fields.char(string='EDI Sender Code',), - } diff --git a/connector_spscommerce/models/procurement.py b/connector_spscommerce/models/procurement.py index 101f199..999460f 100644 --- a/connector_spscommerce/models/procurement.py +++ b/connector_spscommerce/models/procurement.py @@ -1,58 +1,67 @@ -# -*- coding: utf-8 -*- # Copyright (c) Open Source Integrators # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from openerp.osv import fields, osv +from odoo import fields, models -class procurement_order(osv.osv): - _inherit = 'procurement.order' - - _columns = { - 'po_number': fields.char('Line Item PO Number from Converted 856'), - 'edi_line_num': fields.integer('EDI PO Line #'), - 'asn_shipment': fields.char('ASN Shipment Number'), - 'ship_to_code': fields.char('Ship To Warehouse', - help="Trading Partner Ship to location" - " code."), - 'sale_line_id': fields.many2one('sale.order.line', 'Sale Order Line', - help='Sale Order Line from whence this' - ' Stock Move Was created'), - 'so_id': fields.many2one('sale.order', 'Sale Order', - help='Sale Order from Whence this Invoice was' - ' created'), - 'edi_yes': fields.boolean('From an EDI PO?', readonly=True, - help="Is this order from an EDI purchase" - " order, 850 EDI doc."), - 'est_del_date': fields.date('Estimated Delivery Date', - help="Calculated based on shipping method."), - 'trading_partner_id': fields.many2one('edi.config', 'Trading Partner', - help='EDI Configuration' - ' information for partner'), - 'ship_not_before_date': fields.date('Do Not Ship Before This Date', - help="Do Not Ship Before This" - " Date."), - 'cancel_after_date': fields.date('Cancel if Shipped After This Date', - help="Cancel if Shipped After This" - " Date."), - } - - def _run_move_create(self, cr, uid, procurement, context=None): +class ProcurementRule(models.Model): + _inherit = 'procurement.rule' - res = super(procurement_order, self).\ - _run_move_create(cr, uid, procurement=procurement, context=context) - res['po_number'] = procurement.po_number or '' - res['ship_to_code'] = procurement.ship_to_code or '' - res['asn_shipment'] = procurement.asn_shipment or '' - res['sale_line_id'] = procurement.sale_line_id.id or False - res['edi_line_num'] = procurement.edi_line_num or 0 - res['so_id'] = procurement.so_id.id or False - res['trading_partner_id'] = procurement.trading_partner_id and\ - procurement.trading_partner_id.id or False - res['edi_yes'] = procurement.trading_partner_id and\ - procurement.edi_yes or False - res['ship_not_before_date'] = procurement.trading_partner_id and\ - procurement.ship_not_before_date or False - res['cancel_after_date'] = procurement.trading_partner_id and\ - procurement.cancel_after_date or False + po_number = fields.Char('Line Item PO Number from Converted 856') + edi_line_num = fields.Integer('EDI PO Line #') + asn_shipment = fields.Char('ASN Shipment Number') + ship_to_code = fields.Char( + 'Ship To Warehouse', + help="Trading Partner Ship to location code." + ) + sale_line_id = fields.Many2one( + 'sale.order.line', + 'Sale Order Line', + help='Sale Order Line from whence this Stock Move Was created' + ) + so_id = fields.Many2one( + 'sale.order', + 'Sale Order', + help='Sale Order from Whence this Invoice was created' + ) + edi_yes = fields.Boolean( + 'From an EDI PO?', + readonly=True, + help="Is this order from an EDI purchase order, 850 EDI doc." + ) + est_del_date = fields.Date( + 'Estimated Delivery Date', + help="Calculated based on shipping method." + ) + trading_partner_id = fields.Many2one( + 'edi.config', + 'Trading Partner', + help='EDI Configuration information for partner' + ) + ship_not_before_date = fields.Date( + 'Do Not Ship Before This Date', + help="Do Not Ship Before This Date." + ) + cancel_after_date = fields.Date( + 'Cancel if Shipped After This Date', + help="Cancel if Shipped After This Date." + ) - return res + def _get_stock_move_values(self, product_id, product_qty, product_uom, + location_id, name, origin, values, group_id): + res = super(ProcurementRule, self).\ + _get_stock_move_values(product_id, product_qty, product_uom, + location_id, name, origin, values, group_id) + res['po_number'] = self.po_number or '' + res['ship_to_code'] = self.ship_to_code or '' + res['asn_shipment'] = self.asn_shipment or '' + res['sale_line_id'] = self.sale_line_id.id or False + res['edi_line_num'] = self.edi_line_num or 0 + res['so_id'] = self.so_id.id or False + res['trading_partner_id'] = self.trading_partner_id and\ + self.trading_partner_id.id or False + res['edi_yes'] = self.trading_partner_id and self.edi_yes or False + res['ship_not_before_date'] = self.trading_partner_id and\ + self.ship_not_before_date or False + res['cancel_after_date'] = self.trading_partner_id and\ + self.cancel_after_date or False + return res diff --git a/connector_spscommerce/models/purchase.py b/connector_spscommerce/models/purchase.py index 80c29ef..30c4c27 100644 --- a/connector_spscommerce/models/purchase.py +++ b/connector_spscommerce/models/purchase.py @@ -1,12 +1,14 @@ -# -*- coding: utf-8 -*- # Copyright (c) Open Source Integrators # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from openerp.osv import fields, osv +from odoo import fields, models -class purchase_order_line(osv.osv): + +class PurchaseOrderLine(models.Model): _inherit = "purchase.order.line" - - _columns = { - 'asn_shipment': fields.char('ASN Shipment Number from Converted 856'), - 'po_number': fields.char('Line Item PO Number from Converted 856'), - } + + asn_shipment = fields.Char('ASN Shipment Number from Converted 856') + po_number = fields.Char('Line Item PO Number from Converted 856') + edi_yes = fields.Boolean( + 'From an EDI PO?', + help="Is this order from an EDI purchase order, 850 EDI doc." + ) diff --git a/connector_spscommerce/models/res_company.py b/connector_spscommerce/models/res_company.py new file mode 100644 index 0000000..7a02159 --- /dev/null +++ b/connector_spscommerce/models/res_company.py @@ -0,0 +1,14 @@ +# Copyright (c) Open Source Integrators +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from odoo import fields, models + + +class ResCompany(models.Model): + _inherit = 'res.company' + + header_string = fields.Char('Company Header String') + trading_partner_id = fields.Many2many( + 'edi.config', + string='EDI Trading Partner', + ondelete='cascade' + ) diff --git a/connector_spscommerce/models/res_partner.py b/connector_spscommerce/models/res_partner.py new file mode 100644 index 0000000..cbfa1be --- /dev/null +++ b/connector_spscommerce/models/res_partner.py @@ -0,0 +1,21 @@ +# Copyright (c) Open Source Integrators +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from odoo import fields, models + + +class ResPartner(models.Model): + _inherit = 'res.partner' + + trading_partner_res = fields.One2many( + 'edi.config', + 'trading_partner_id', + required=False, + ondelete='cascade', + ) + edi_ids = fields.One2many( + 'edi.config', + 'edi_company_id', + 'EDI Company ID' + ) + ship_to_code = fields.Char('Ship to Code') + sender_id = fields.Char('EDI Sender Code') diff --git a/connector_spscommerce/models/sale.py b/connector_spscommerce/models/sale.py index 43967b9..c5d5c24 100644 --- a/connector_spscommerce/models/sale.py +++ b/connector_spscommerce/models/sale.py @@ -1,201 +1,452 @@ -# -*- coding: utf-8 -*- # Copyright (c) Open Source Integrators # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). import re from datetime import datetime, timedelta from random import randint -from openerp import api, models -import openerp.addons.decimal_precision as dp -from openerp.osv import fields, osv -from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT +from odoo import api, fields, models +from odoo.addons import decimal_precision as dp +from odoo.tools import DEFAULT_SERVER_DATETIME_FORMAT as DS +from odoo.exceptions import UserError class SaleOrder(models.Model): _inherit = "sale.order" - _columns = { - 'asn_shipment': fields.char('ASN Number from 856'), - 'edi_error': fields.text('EDI Errors', help="Text that will describe failures to add sale order lines because of failing product lookups."), - 'edi_yes': fields.boolean('From an EDI PO?', help="Is this order from an EDI purchase order, 850 EDI doc."), - 'ack_yes': fields.boolean('855', help="Will this order have an 855?"), - '855_replace': fields.boolean('Send 855', readonly=True, help="Shall we send an 855 replacement?"), - 'ship_to_code': fields.char('Ship To Warehouse', help="Trading Partner Ship to location code."), - 'supplier_code': fields.char('Supplier Code', help="Supplier code from the 856."), - 'ship_not_before_date': fields.date('Estimated Shipping Date', help="This is the date from the 856."), - 'cancel_after_date': fields.date('Cancel if Shipped After This Date', help="Cancel if Shipped After This Date."), - 'trading_partner_id': fields.many2one('edi.config', 'Trading Partner', help='EDI Configuration information for partner'), - '855_sent_timestamp': fields.datetime('855 Sent Date', help="The timestamp for when the 855 was sent."), - '855_check': fields.boolean('855 Sent Already', help="A check to see if 855 has been sent already."), - 'scac_code': fields.char('SCAC Code', help="This is the shipping alpha code from your carrier."), - 'bol_num': fields.char('BoL Number', help="This is bill of lading number from your carrier/shipper."), - 'tracking_num': fields.char('Tracking Number', help="This is the tracking number from your carrier."), - 'tset_purpose_code':fields.char('TsetPurposeCode', help="Code identifying purpose of the document.."), - 'purchase_order_type_code':fields.char('PurchaseOrderTypeCode', help="Code specifying the type of purchase order."), - 'po_type_description':fields.char('POTypeDescription', help="Free form text to describe the type of order."), - 'ship_complete_code':fields.char('ShipCompleteCode', help="Code to identify a specific requirement or agreement of sale. Should only be used to indicate if an item can be placed on backorder."), - 'department':fields.char('Department', help="Name or number identifying an area wherein merchandise is categorized within a store."), - 'division':fields.char('Division', help="Different entities belonging to the same parent company."), - 'promotion_deal_number':fields.char('PromotionDealNumber', help="Number uniquely identifying an agreement for a special offer or price."), - 'terms_type':fields.char('TermsType', help="Code identifying type of payment terms."), - 'terms_basis_date_code':fields.char('TermsBasisDateCode', help="Code identifying the beginning of the terms period."), - 'terms_discount_percentage':fields.char('TermsDiscountPercentage', help="Terms discount percentage available to the purchaser"), - 'terms_discount_due_days':fields.char('TermsDiscountDueDays', help="Number of days by which payment or invoice must be received in order to receive the discount noted."), - 'terms_net_due_days':fields.char('TermsNetDueDays', help="Number of days until total invoice amount is due[discount not applicable."), - 'payment_method_code':fields.char('PaymentMethodCode', help="Indication of the instrument of payment."), - 'fob_pay_code':fields.char('FOBPayCode', help="Code identifying payment terms for transportation charges."), - 'fob_location_qualifier':fields.char('FOBLocationQualifier', help="Code identifying type of location at which ownership of goods is transferred."), - 'fob_location_description':fields.char('FOBLocationDescription', help="Free-form textual description of the location at which ownership of goods is transferred."), - 'fob_title_passage_code':fields.char('FOBTitlePassageCode', help="Code describing the location of ownership of the goods."), - 'fob_title_passage_location':fields.char('FOBTitlePassageLocation', help="Location of ownership of the goods."), - 'carrier_trans_method_code':fields.char('CarrierTransMethodCode', help="Code specifying the method or type of transportation for the shipment."), - 'carrier_alpha_code':fields.char('CarrierAlphaCode', help="Standard Carrier Alpha Code[SCAC] - "), - 'carrier_routing':fields.char('CarrierRouting', help="Free-form description of the routing/requested routing for shipment or the originating carrier's identity."), - 'routing_sequence_code':fields.char('RoutingSequenceCode', help=""), - 'service_level_code':fields.char('ServiceLevelCode', help="Code indicating the level of transportation service or the billing service offered by the transportation carrier."), - 'reference_qual':fields.char('ReferenceQual', help="Code specifying the type of data in the ReferenceID/ReferenceDescription."), - 'reference_id':fields.char('ReferenceID', help="Code specifying the type of data in the ReferenceID/ReferenceDescription."), - 'ref_description':fields.char('Description', help="Free-form textual description to clarify the related data elements and their content."), - 'note_code':fields.char('NoteCode', help="Code specifying the type of note."), - 'note_information_field':fields.char('NoteInformationField', help="Free-form textual description of the note."), - 'allow_chrg_indicator':fields.char('AllowChrgIndicator', help="Code which indicates an allowance or charge for the service specified."), - 'allow_chrg_code':fields.char('AllowChrgCode', help="Code describing the type of allowance or charge for the service specified."), - 'allow_chrg_agency_code':fields.char('AllowChrgAgencyCode', help="Code identifying the agency assigning the code values."), - 'allow_chrg_agency':fields.char('AllowChrgAgency', help="Agency maintained code identifying the service, promotion, allowance, or charge."), - 'allow_chrg_amt':fields.float('AllowChrgAmt', help="Amount of the allowance or charge."), - 'allow_chrg_percent_qual':fields.char('AllowChrgPercentQual', help="Code indicating on what basis an allowance or charge percent is calculated.."), - 'allow_chrg_percent':fields.float('AllowChrgPercent', help="Percentage of allowance or charge. Percentages should be represented as real numbers[0% through 100% should be normalized to 0.0 through 100.00].."), - 'allow_chrg_handling_code':fields.char('AllowChrgHandlingCode', help="Code indicating method of handling for an allowance or charge.."), - 'reference_identification':fields.char('ReferenceIdentification', help=""), - 'allow_chrg_handling_description':fields.char('AllowChrgHandlingDescription', help="Free-form textual description of the note."), - } - - def create_855(self, cr, uid, sale_ids, context=None): - - xml_output = 'testVersion
' - - for sale_id in sale_ids: - + asn_shipment = fields.Char('ASN Number from 856') + edi_error = fields.Text( + 'EDI Errors', + help="Text that will describe failures to add sale order lines" + "because of failing product lookups." + ) + edi_yes = fields.Boolean( + 'From an EDI PO?', + help="Is this order from an EDI purchase order, 850 EDI doc." + ) + ack_yes = fields.Boolean( + '855', + help="Will this order have an 855?" + ) + replace = fields.Boolean( + 'Send 855', + readonly=True, + help="Shall we send an 855 replacement?" + ) + ship_to_code = fields.Char( + 'Ship To Warehouse', + help="Trading Partner Ship to location code." + ) + supplier_code = fields.Char( + 'Supplier Code', + help="Supplier code from the 856." + ) + ship_not_before_date = fields.Date( + 'Estimated Shipping Date', + help="This is the date from the 856." + ) + cancel_after_date = fields.Date( + 'Cancel if Shipped After This Date', + help="Cancel if Shipped After This Date." + ) + trading_partner_id = fields.Many2one( + 'edi.config', + 'Trading Partner', + help='EDI Configuration information for partner' + ) + sent_timestamp = fields.Datetime( + '855 Sent Date', + help="The timestamp for when the 855 was sent." + ) + check = fields.Boolean( + '855 Sent Already', + help="A check to see if 855 has been sent already." + ) + scac_code = fields.Char( + 'SCAC Code', + help="This is the shipping alpha code from your carrier." + ) + bol_num = fields.Char( + 'BoL Number', + help="This is bill of lading number from your carrier/shipper." + ) + tracking_num = fields.Char( + 'Tracking Number', + help="This is the tracking number from your carrier." + ) + tset_purpose_code = fields.Char( + 'Tset Purpose Code', + help="Code identifying purpose of the document.." + ) + purchase_order_type_code = fields.Char( + 'Purchase Order Type Code', + help="Code specifying the type of purchase order." + ) + po_type_description = fields.Char( + 'PO Type Description', + help="Free form text to describe the type of order." + ) + ship_complete_code = fields.Char( + 'Ship Complete Code', + help='''Code to identify a specific requirement or agreement of sale. + Should only be used to indicate if an item can be + placed on backorder.''' + ) + department = fields.Char( + 'Department', + help="Name or number identifying an area wherein merchandise is" + "categorized within a store." + ) + division = fields.Char( + 'Division', + help="Different entities belonging to the same parent company." + ) + promotion_deal_number = fields.Char( + 'Promotion Deal Number', + help="Number uniquely identifying an agreement for a special" + "offer or price." + ) + terms_type = fields.Char( + 'Terms Type', + help="Code identifying type of payment terms." + ) + terms_basis_date_code = fields.Char( + 'Terms Basis Date Code', + help="Code identifying the beginning of the terms period." + ) + terms_discount_percentage = fields.Char( + 'Terms Discount Percentage', + help="Terms discount percentage available to the purchaser" + ) + terms_discount_due_days = fields.Char( + 'Terms Discount Due Days', + help="Number of days by which payment or invoice must be received in" + "order to receive the discount noted." + ) + terms_net_due_days = fields.Char( + 'Terms Net Due Days', + help="Number of days until total invoice amount is" + "due[discount not applicable." + ) + payment_method_code = fields.Char( + 'Payment Method Code', + help="Indication of the instrument of payment." + ) + fob_pay_code = fields.Char( + 'FOB Pay Code', + help="Code identifying payment terms for transportation Charges." + ) + fob_location_qualifier = fields.Char( + 'FOB Location Qualifier', + help="Code identifying type of location at which ownership of goods" + "is transferred." + ) + fob_location_description = fields.Char( + 'FOB Location Description', + help="Free-form textual description of the location at which" + "ownership of goods is transferred." + ) + fob_title_passage_code = fields.Char( + 'FOB Title Passage Code', + help="Code describing the location of ownership of the goods." + ) + fob_title_passage_location = fields.Char( + 'FOB Title Passage Location', + help="Location of ownership of the goods." + ) + carrier_trans_method_code = fields.Char( + 'Carrier Trans Method Code', + help="Code specifying the method or type of transportation for" + "the shipment." + ) + carrier_alpha_code = fields.Char( + 'Carrier Alpha Code', + help="Standard Carrier Alpha Code[SCAC] - " + ) + carrier_routing = fields.Char( + 'Carrier Routing', + help="Free-form description of the routing/requested routing for" + "shipment or the originating carrier's identity." + ) + routing_sequence_code = fields.Char('Routing Sequence Code') + service_level_code = fields.Char( + 'Service Level Code', + help="Code indicating the level of transportation service or the" + "billing service offered by the transportation carrier." + ) + reference_qual = fields.Char( + 'Reference Qual', + help="Code specifying the type of data in the" + "ReferenceID/ReferenceDescription." + ) + reference_id = fields.Char( + 'Reference ID', + help="Code specifying the type of data in the" + "ReferenceID/ReferenceDescription." + ) + ref_description = fields.Char( + 'Description', + help="Free-form textual description to clarify the related" + "data elements and their content." + ) + note_code = fields.Char( + 'Note Code', + help="Code specifying the type of note." + ) + note_information_field = fields.Char( + 'Note Information Field', + help="Free-form textual description of the note." + ) + allow_chrg_indicator = fields.Char( + 'Allow Chrg Indicator', + help="Code which indicates an allowance or charge for" + "the service specified." + ) + allow_chrg_code = fields.Char( + 'Allow Chrg Code', + help="Code describing the type of allowance or charge" + "for the service specified." + ) + allow_chrg_agency_code = fields.Char( + 'Allow Chrg Agency Code', + help="Code identifying the agency assigning the code values." + ) + allow_chrg_agency = fields.Char( + 'Allow Chrg Agency', + help="Agency maintained code identifying the service," + "promotion, allowance, or charge." + ) + allow_chrg_amt = fields.Float( + 'Allow Chrg Amt', + help="Amount of the allowance or charge." + ) + allow_chrg_percent_qual = fields.Char( + 'Allow Chrg Percent Qual', + help="Code indicating on what basis an allowance or charge" + "percent is calculated.." + ) + allow_chrg_percent = fields.Float( + 'Allow Chrg Percent', + help='''Percentage of allowance or charge. Percentages should be + represented as real numbers[0% through 100% should be normalized + to 0.0 through 100.00]..''' + ) + allow_chrg_handling_code = fields.Char( + 'Allow Chrg Handling Code', + help="Code indicating method of handling for an allowance or charge.." + ) + reference_identification = fields.Char('ReferenceIdentification') + allow_chrg_handling_description = fields.Char( + 'Allow Chrg Handling Description', + help="Free-form textual description of the note." + ) + + @api.multi + def create_855(self): + xml_output = ''' + + testVersion
''' + for sale_id in self: + # initialize - csvwriter = None - sale_obj = self.browse(cr, uid, sale_id, context=context) - config_obj = sale_obj.trading_partner_id + # csvwriter = None + # sale_obj = self.browse(cr, uid, sale_id, context=context) + config_obj = sale_id.trading_partner_id OUT_PATH = config_obj.out_path - sale_lines = sale_obj.order_line - sale_name = sale_obj.name - num = re.findall('\d+', sale_name)[0] + sale_lines = sale_id.order_line + sale_name = sale_id.name + num = re.findall('\d+', sale_name)[0] po_num = '' - - if sale_obj.client_order_ref: - po_num = sale_obj.client_order_ref - + if sale_id.client_order_ref: + po_num = sale_id.client_order_ref + # create and format dates for ACK now = datetime.now() today = now.strftime('%Y%m%d') - time = datetime.now().strftime('%H%M%S') - po_date = datetime.strptime(sale_obj.date_order, '%Y-%m-%d %H:%M:%S') + # time = datetime.now().strftime('%H%M%S') + po_date = datetime.strptime(sale_id.date_order, DS) po_date = po_date.strftime('%m/%d/%Y') - + # generate random numbers for the 3 header id numbers in 855 ISA_num = randint(100000000, 999999999) GS_code = randint(1000000, 9999999) st_trans_num = randint(100000000, 999999999) - # Grab the vendor_id and trading_partner_id for EDI in the edi_config. - trading_partner_code = sale_obj.trading_partner_id.partner_header_string - trading_partner_padded = trading_partner_code.ljust(15) + # Grab the vendor_id and trading_partner_id for EDI + # in the edi_config. + trading_partner_code =\ + sale_id.trading_partner_id.partner_header_string or 'Test' + if trading_partner_code: + trading_partner_padded = trading_partner_code.ljust(15) + + vendor_code =\ + sale_id.trading_partner_id.vendor_header_string or 'Test' + if vendor_code: + vendor_padded = vendor_code.ljust(15) + OUT_PATH = str(sale_id.trading_partner_id.out_path) - vendor_code = sale_obj.trading_partner_id.vendor_header_string - vendor_padded = vendor_code.ljust(15) - OUT_PATH = str(sale_obj.trading_partner_id.out_path) - # header, payment and date info - xml_output += '' + trading_partner_code + '' - xml_output += '' + po_num + '' - xml_output += '' + str(sale_obj.tset_purpose_code) + '' - xml_output += '' + po_date + '' - xml_output += '' + str(sale_obj.purchase_order_type_code) + '' - xml_output += '' + str(sale_obj.po_type_description) + '' + xml_output += '' +\ + trading_partner_code + '' + xml_output += '' +\ + po_num + '' + xml_output += '' +\ + str(sale_id.tset_purpose_code) + '' + xml_output += '' +\ + po_date + '' + xml_output += '' +\ + str(sale_id.purchase_order_type_code + ) + '' + xml_output += '' +\ + str(sale_id.po_type_description) + '' xml_output += '' + po_num + '' xml_output += 'RJ' - xml_output += '' + today + '' - xml_output += '' + str(sale_obj.ship_complete_code) + '' - xml_output += '' + str(sale_obj.pricelist_id.currency_id.name) + '' - xml_output += '' + str(sale_obj.department) + '' - xml_output += '' + str(sale_obj.division) + '' - xml_output += '' + str(sale_obj.name) + '' - xml_output += '' + str(sale_obj.promotion_deal_number) + '' + xml_output += '' +\ + today + '' + xml_output += '' +\ + str(sale_id.ship_complete_code) + '' + xml_output += '' +\ + str(sale_id.pricelist_id.currency_id.name + ) + '' + xml_output += '' +\ + str(sale_id.department) + '' + xml_output += '' +\ + str(sale_id.division) + '' + xml_output += '' +\ + str(sale_id.name) + '' + xml_output += '' +\ + str(sale_id.promotion_deal_number) + '' xml_output += '' + vendor_code + '' - xml_output += '' + str(sale_obj.terms_type) + '' - xml_output += '' + str(sale_obj.terms_basis_date_code) + '' - xml_output += '' + str(sale_obj.terms_discount_percentage) + '' - xml_output += '' + str(sale_obj.terms_discount_due_days) + '' - xml_output += '' + str(sale_obj.terms_net_due_days) + '' - xml_output += '' + str(sale_obj.payment_term_id.note) + '' - xml_output += '' + str(sale_obj.payment_method_code) + '' + xml_output += '' +\ + str(sale_id.terms_type) + '' + xml_output += '' +\ + str(sale_id.terms_basis_date_code) + '' + xml_output += '' +\ + str(sale_id.terms_discount_percentage + ) + '' + xml_output += '' +\ + str(sale_id.terms_discount_due_days + ) + '' + xml_output += '' +\ + str(sale_id.terms_net_due_days + ) + '' + xml_output += '' +\ + str(sale_id.payment_term_id.note) + '' + xml_output += '' +\ + str(sale_id.payment_method_code + ) + '' xml_output += 'ORS' xml_output += '' + today + '' - cust_rec = sale_obj.partner_id - ship_address_rec = sale_obj.partner_shipping_id - + cust_rec = sale_id.partner_id + ship_address_rec = sale_id.partner_shipping_id + # customer contact details xml_output += 'CH' - xml_output += '' + str(cust_rec.name) + '' - xml_output += '' + str(cust_rec.phone) + '' - xml_output += '' + str(cust_rec.fax) + '' - xml_output += '' + str(cust_rec.email) + '' + xml_output += '' +\ + str(cust_rec.name) + '' + xml_output += '' +\ + str(cust_rec.phone) + '' +# xml_output += '' + str(cust_rec.fax) + '' + xml_output += '' +\ + str(cust_rec.email) + '' # contact address details xml_output += '
FW' xml_output += '1' - xml_output += '11111' - xml_output += '' + str(ship_address_rec.name) + '' - xml_output += '' + str(ship_address_rec.street) + '' - xml_output += '' + str(ship_address_rec.street2) + '' - xml_output += '' + str(ship_address_rec.city) + '' - xml_output += '' + str(ship_address_rec.state_id.code) + '' - xml_output += '' + str(ship_address_rec.zip) + '' - xml_output += '' + str(ship_address_rec.country_id.code) + '' + xml_output +=\ + '11111' + xml_output += '' +\ + str(ship_address_rec.name) + '' + xml_output += '' +\ + str(ship_address_rec.street) + '' + xml_output += '' +\ + str(ship_address_rec.street2) + '' + xml_output += '' +\ + str(ship_address_rec.city) + '' + xml_output += '' +\ + str(ship_address_rec.state_id.code) + '' + xml_output += '' +\ + str(ship_address_rec.zip) + '' + xml_output += '' +\ + str(ship_address_rec.country_id.code) + '' xml_output += 'CH' - xml_output += '' + str(cust_rec.name) + '' - xml_output += '' + str(cust_rec.phone) + '' - xml_output += '' + str(cust_rec.fax) + '' - xml_output += '' + str(cust_rec.email) + '
' - + xml_output += '' +\ + str(cust_rec.name) + '' + xml_output += '' +\ + str(cust_rec.phone) + '' +# xml_output += '' + str(cust_rec.fax) + '' + xml_output += '' +\ + str(cust_rec.email) + '' + # FOBRelatedInstruction - xml_output += '' + str(sale_obj.fob_pay_code) + '' - xml_output += '' + str(sale_obj.fob_location_qualifier) + '' - xml_output += '' + str(sale_obj.fob_location_description) + '' - xml_output += '' + str(sale_obj.fob_title_passage_code) + '' - xml_output += '' + str(sale_obj.fob_title_passage_location) + '' - + xml_output += '' +\ + str(sale_id.fob_pay_code) + '' + xml_output += '' +\ + str(sale_id.fob_location_qualifier) + '' + xml_output += '' +\ + str(sale_id.fob_location_description + ) + '' + xml_output += '' +\ + str(sale_id.fob_title_passage_code) + '' + xml_output += '' +\ + str(sale_id.fob_title_passage_location + ) + '' + # CarrierInformation - xml_output += '' + str(sale_obj.carrier_trans_method_code) + '' - xml_output += '' + str(sale_obj.carrier_alpha_code) + '' - xml_output += '' + str(sale_obj.carrier_routing) + '' - xml_output += '' + str(sale_obj.routing_sequence_code) + '' - xml_output += '' + str(sale_obj.service_leve_code) + '' - + xml_output += '' +\ + str(sale_id.carrier_trans_method_code + ) + '' + xml_output += '' +\ + str(sale_id.carrier_alpha_code) + '' + xml_output += '' +\ + str(sale_id.carrier_routing) + '' + xml_output += '' +\ + str(sale_id.routing_sequence_code) + '' +# xml_output += '' +\ +# str(sale_id.service_leve_code +# ) + ''' +# ''' + # Reference - xml_output += '' + str(sale_obj.reference_qual) + '' - xml_output += '' + str(sale_obj.reference_id) + '' - xml_output += '' + str(sale_obj.ref_description) + '' - + xml_output += '' +\ + str(sale_id.reference_qual) + '' + xml_output += '' +\ + str(sale_id.reference_id) + '' + xml_output += '' +\ + str(sale_id.ref_description) + '' + # Notes - xml_output += '' + str(sale_obj.note_code) + '' - xml_output += '' + str(sale_obj.note_information_field) + '' - + xml_output += '' +\ + str(sale_id.note_code) + '' + xml_output += '' +\ + str(sale_id.note_information_field + ) + '' + # ChargesAllowances - xml_output += '' + str(sale_obj.allow_chrg_indicator) + '' - xml_output += '' + str(sale_obj.allow_chrg_code) + '' - xml_output += '' + str(sale_obj.allow_chrg_agency_code) + '' - xml_output += '' + str(sale_obj.allow_chrg_agency) + '' - xml_output += '' + str(sale_obj.allow_chrg_amt) + '' - xml_output += '' + str(sale_obj.allow_chrg_percent_qual) + '' - xml_output += '' + str(sale_obj.allow_chrg_percent) + '' - xml_output += '' + str(sale_obj.allow_chrg_handling_code) + '' - xml_output += '' + str(sale_obj.reference_identification) + '' - xml_output += '' + str(sale_obj.allow_chrg_handling_description) + '
' - + xml_output += '' +\ + str(sale_id.allow_chrg_indicator) + '' + xml_output += '' +\ + str(sale_id.allow_chrg_code) + '' + xml_output += '' +\ + str(sale_id.allow_chrg_agency_code) + '' + xml_output += '' +\ + str(sale_id.allow_chrg_agency) + '' + xml_output += '' +\ + str(sale_id.allow_chrg_amt) + '' + xml_output += '' +\ + str(sale_id.allow_chrg_percent_qual + ) + '' + xml_output += '' +\ + str(sale_id.allow_chrg_percent) + '' + xml_output += '' +\ + str(sale_id.allow_chrg_handling_code + ) + '' + xml_output += '' +\ + str(sale_id.reference_identification + ) + '' + xml_output += '' +\ + str(sale_id.allow_chrg_handling_description + ) + '' +\ + '
' + total_lines = 0 total_qty = 0 total_weight = 0 @@ -205,309 +456,400 @@ def create_855(self, cr, uid, sale_ids, context=None): est_del_date = '' est_ship_date = '' accept_code = '' - + if line.edi_est_ship_date: - est_ship_date = datetime.strptime(line.edi_est_ship_date, '%Y-%m-%d') + est_ship_date =\ + datetime.strptime(line.edi_est_ship_date, '%Y-%m-%d') est_ship_date = est_ship_date.strftime('%m/%d/%Y') - + if line.edi_line_msg == 'reject': accept_code = 'CC' - + elif line.edi_line_msg == 'backorder': accept_code = 'IB' - else: accept_code = 'IA' - - uom = line.product_uom.name + + uom = line.product_uom.name quantity = int(line.product_uom_qty) total_qty += quantity - total_weight += (line.product_id.weight * quantity) + total_weight += (line.product_id.weight * quantity) original_po_qty = int(line.edi_line_qty) - + if uom == 'Unit(s)': uom = 'EA' - else: - print "***** UOM is Not EA (Each) *****" - + print("***** UOM is Not EA (Each) *****") + # line items xml_output += '' - xml_output += '' + str(line.id) + '' - xml_output += '' + str(line.buyer_part_number) + '' - xml_output += '' + str(line.vendor_part_number) + '' - xml_output += '' + str(line.consumer_package_code) + '' + xml_output += '' +\ + str(line.id) + '' + xml_output += '' +\ + str(line.buyer_part_number) + '' + xml_output += '' +\ + str(line.vendor_part_number) + '' + xml_output += '' +\ + str(line.consumer_package_code) + '' xml_output += '' + str(line.gtin) + '' - xml_output += '' + str(line.upc_case_code) + '' + xml_output += '' +\ + str(line.upc_case_code) + '' xml_output += 'MN' - xml_output += '' + str(line.product_id.default_code) + '' + xml_output += '' +\ + str(line.product_id.default_code + ) + '' xml_output += '' + str(uom) + '' - xml_output += '' + str(line.edi_line_qty) + '' - xml_output += '' + str(line.price_unit) + '' - xml_output += '' + str(line.purchase_price_basis) + '' - xml_output += '' + str(sale_obj.pricelist_id.currency_id.name) + '' - xml_output += '' + str(line.product_size_code) + '' - xml_output += '' + str(line.product_size_description) + '' - xml_output += '' + str(line.product_color_code) + '' - xml_output += '' + str(line.product_color_description) + '' - xml_output += '' + str(line.product_material_code) + '' - xml_output += '' + str(line.product_material_description) + '' - xml_output += '' + str(line.department) + '' - xml_output += '' + str(line.classs) + '' - + xml_output += '' +\ + str(line.edi_line_qty) + '' + xml_output += '' + str(line.price_unit + ) + '' + xml_output += '' +\ + str(line.purchase_price_basis) + '' + xml_output += '' +\ + str(sale_id.pricelist_id.currency_id.name + ) + '' + xml_output += '' +\ + str(line.product_size_code) + '' + xml_output += '' +\ + str(line.product_size_description + ) + '' + xml_output += '' +\ + str(line.product_color_code) + '' + xml_output += '' +\ + str(line.product_color_description + ) + '' + xml_output += '' +\ + str(line.product_material_code) + '' + xml_output += '' +\ + str(line.product_material_description + ) + '' + xml_output += '' + str(line.department + ) + '' + xml_output += '' + str(line.classs + ) + '' + # date - xml_output += '' + str(line.price_unit) + '' + xml_output += '' +\ + str(line.price_unit) + '' xml_output += '' + today + '' - + # PriceInformation - xml_output += '' + str(line.price_unit) + '' - xml_output += '' + str(line.price_unit) + '' + xml_output += '' +\ + str(line.price_unit) + '' + xml_output += '' + str(line.price_unit + ) + '' xml_output += '' + str(quantity) + '' - xml_output += '' + str(line.multiple_price_quantity) + '' - xml_output += '' + str(line.class_of_trade_code) + '' - + xml_output += '' +\ + str(line.multiple_price_quantity + ) + '' + xml_output += '' +\ + str(line.class_of_trade_code + ) + '' + # ProductOrItemDescription - xml_output += '' + str(line.item_description_type) + '' - xml_output += '' + str(line.product_characteristic_code) + '' - xml_output += '' + str(line.agency_qualifier_code) + '' - xml_output += '' + str(line.product_description_code) + '' - xml_output += '' + str(line.name) + '' - + xml_output +=\ + '' +\ + str(line.item_description_type) + '' + xml_output += '' +\ + str(line.product_characteristic_code + ) + '' + xml_output += '' +\ + str(line.agency_qualifier_code) + '' + xml_output += '' +\ + str(line.product_description_code + ) + '' + xml_output += '' +\ + str(line.name + ) + '' + # PhysicalDetails - xml_output += '' + str(line.pack_qualifier) + '' - xml_output += '' + str(line.pack_value) + '' - xml_output += '' + str(line.pack_size) + '' + xml_output += '' +\ + str(line.pack_qualifier) + '' + xml_output += '' + str(line.pack_value + ) + '' + xml_output += '' + str(line.pack_size + ) + '' xml_output += '' + str(line.pack_uom) + '' - xml_output += '' + str(line.packing_medium) + '' - xml_output += '' + str(line.packing_material) + '' - xml_output += '' + str(line.pack_weight) + '' - xml_output += '' + str(line.pack_weight_uom) + '' - + xml_output += '' + str(line.packing_medium + ) + '' + xml_output += '' +\ + str(line.packing_material) + '' + xml_output += '' + str(line.pack_weight + ) + '' + xml_output += '' +\ + str(line.pack_weight_uom + ) + '' + # Reference - xml_output += '' + str(sale_obj.reference_qual) + '' - xml_output += '' + str(sale_obj.reference_id) + '' - xml_output += '' + str(sale_obj.ref_description) + '' - + xml_output += '' +\ + str(sale_id.reference_qual) + '' + xml_output += '' + str(sale_id.reference_id + ) + '' + xml_output += '' +\ + str(sale_id.ref_description) + '' + # Notes - xml_output += '' + str(sale_obj.note_code) + '' - xml_output += '' + str(sale_obj.note_information_field) + '' - + xml_output += '' + str(sale_id.note_code + ) + '' + xml_output += '' +\ + str(sale_id.note_information_field + ) + '' + # contact address details xml_output += '
FW' - xml_output += '' + str(ship_address_rec.name) + '' - xml_output += '' + str(ship_address_rec.street) + '' + xml_output += '' + str(ship_address_rec.name + ) + '' + xml_output += '' + str(ship_address_rec.street + ) + '' xml_output += '' + str(ship_address_rec.city) + '' - xml_output += '' + str(ship_address_rec.state_id.code) + '' - xml_output += '' + str(ship_address_rec.zip) + '' - xml_output += '' + str(ship_address_rec.country_id.code) + '
' - + xml_output += '' + str(ship_address_rec.state_id.code + ) + '' + xml_output += '' + str(ship_address_rec.zip + ) + '' + xml_output += '' +\ + str(ship_address_rec.country_id.code + ) + '' + # ChargesAllowances - xml_output += '' + str(line.allow_chrg_indicator) + '' - xml_output += '' + str(line.allow_chrg_code) + '' - xml_output += '' + str(line.allow_chrg_agency_code) + '' - xml_output += '' + str(line.allow_chrg_agency) + '' - xml_output += '' + str(line.allow_chrg_amt) + '' - xml_output += '' + str(line.allow_chrg_percent) + '' - xml_output += '' + str(line.percent_dollar_basis) + '' - xml_output += '' + str(line.allow_chrg_rate) + '' - xml_output += '' + str(line.allow_chrg_qty_uom) + '' - xml_output += '' + str(line.allow_chrg_handling_code) + '' - xml_output += '' + str(line.allow_chrg_handling_description) + '' - - # line item acknowledgement + xml_output += '' +\ + str(line.allow_chrg_indicator) + '' + xml_output += '' + str(line.allow_chrg_code + ) + '' + xml_output += '' +\ + str(line.allow_chrg_agency_code) + '' + xml_output += '' +\ + str(line.allow_chrg_agency) + '' + xml_output += '' +\ + str(line.allow_chrg_amt) + '' + xml_output += '' +\ + str(line.allow_chrg_percent) + '' + xml_output += '' +\ + str(line.percent_dollar_basis) + '' + xml_output += '' +\ + str(line.allow_chrg_rate) + '' + xml_output += '' +\ + str(line.allow_chrg_qty_uom) + '' + xml_output += '' +\ + str(line.allow_chrg_handling_code + ) + '' + xml_output += '' +\ + str(line.allow_chrg_handling_description) +\ + '' + + # line item acknowledgement xml_output += '' - xml_output += '' + str(line.edi_line_msg) + '' - xml_output += '' + str(quantity) + '' - xml_output += '' + str(uom) + '' - xml_output += '002' - xml_output += '' + str(line.edi_est_ship_date) + '' - - xml_output += 'FCP' - xml_output += '' + str(line.price_unit) + '' - + xml_output += '' +\ + str(line.edi_line_msg) + '' + xml_output += '' +\ + str(quantity) + '' + xml_output += '' +\ + str(uom) + '' + xml_output +=\ + '002' + xml_output += '' +\ + str(line.edi_est_ship_date + ) + '' + + xml_output +=\ + 'FCP' + xml_output += '' +\ + str(line.price_unit) + '' + for tax in line.tax_id: - xml_output += 'H780' - xml_output += '' + str(1 + tax.amount / 100 * line.price_unit) + '' - xml_output += '' + str(tax.amount / 100) + '099990000' - + xml_output += '' +\ + str(1 + tax.amount / 100 * line.price_unit + ) + '' + xml_output += '' +\ + str(tax.amount / 100) +\ + '0' +\ + '99990000' xml_output += '
' - + xml_output += '
' - + # Summary - xml_output += '' + str(sale_obj.amount_total) + '' - xml_output += '' + str(total_lines) + '' - xml_output += '' + str(total_qty) + '' - xml_output += '' + str(total_weight) + '' + xml_output += '' + str(sale_id.amount_total + ) + '' + xml_output += '' +\ + str(total_lines) + '' + xml_output += '' + str(total_qty + ) + '' + xml_output += '' + str(total_weight + ) + '' xml_output += '0' xml_output += '0' xml_output += '0' - + date_format = "%Y-%m-%d %H:%M:%S" now = datetime.now() - today = now.strftime(date_format) - sale_obj.write({'855_check':True, '855_sent_timestamp':today}) - print "***** SUCCESSFULLY STORED *****" - + today = now.strftime(date_format) + sale_id.write({'check': True, 'sent_timestamp': today}) + print("***** SUCCESSFULLY STORED *****") + xml_output += '' - - # Write ASN doc to text file + + # Write ASN doc to text file filename = '855_' + today + '%s.txt' % num filename.replace('/', '_') + if not OUT_PATH: + raise UserError('Add out path in Trading Partner') fd = open(OUT_PATH + '/' + filename, 'w') fd.write(xml_output) fd.close() - return True - - def _create_855_wrapper(self, cr, uid, context=None): - - # search for invoices that are ack_yes = True, edi_yes = True and 855_sent_timestamp = False - eligible_orders = self.search(cr, uid, [('ack_yes', '=', True), ('edi_yes', '=', True), ('855_sent_timestamp', '=', False)], context=context) - - return eligible_orders and self.create_855(cr, uid, eligible_orders, context=context) or False - - def action_send_855(self, cr, uid, ids, context=None): + + def _create_855_wrapper(self): + # search for invoices that are ack_yes = True, + # edi_yes = True and sent_timestamp = False + eligible_orders = self.search([ + ('ack_yes', '=', True), ('edi_yes', '=', True), + ('sent_timestamp', '=', False) + ]) + return eligible_orders and eligible_orders.create_855() or False + + @api.multi + def action_send_855(self): """ Creates and new 855 and puts it into the outbox """ - if context is None: - context = {} - # execute the create_855 method - self.create_855(cr, uid, ids, context=None) - + self.create_855() return True - - @api.multi - def _prepare_order_line_procurement(self, group_id=False): - - self.ensure_one() - res = super(SaleOrder, self)._prepare_order_line_procurement(group_id=group_id) - - for sale_line in self.order_line: - - res['po_number'] = sale_line.po_number or '' - res['asn_shipment'] = sale_line.asn_shipment or '' - res['sale_line_id'] = sale_line.id or False - res['edi_line_num'] = sale_line.edi_line_num or 0 - res['so_id'] = self.id or False - res['ship_to_code'] = self.ship_to_code or '' - res['trading_partner_id'] = self.trading_partner_id and self.trading_partner_id.id or False - res['edi_yes'] = self.trading_partner_id and self.edi_yes or False - res['ship_not_before_date'] = self.trading_partner_id and self.ship_not_before_date or False - res['cancel_after_date'] = self.trading_partner_id and self.cancel_after_date or False - - return res - @api.model - def _prepare_procurement_group(self): +# @api.model +# def _prepare_procurement_group(self): +# res = super(SaleOrder, self)._prepare_procurement_group() +# res['trading_partner_id'] =\ +# self.trading_partner_id and self.trading_partner_id.id or False +# # res['edi_yes'] = self.trading_partner_id and self.edi_yes or False +# res['edi_yes'] = True +# res['asn_shipment'] = self.asn_shipment or 'asn_id' +# res['ship_to_code'] = self.ship_to_code or '' +# res['ship_not_before_date'] =\ +# self.trading_partner_id and self.ship_not_before_date or False +# res['cancel_after_date'] =\ +# self.trading_partner_id and self.cancel_after_date or False +# res['po_number'] = self.client_order_ref or '' +# return res - res = super(SaleOrder, self)._prepare_procurement_group() - res['trading_partner_id'] = self.trading_partner_id and self.trading_partner_id.id or False - # res['edi_yes'] = self.trading_partner_id and self.edi_yes or False - res['edi_yes'] = True - res['asn_shipment'] = self.asn_shipment or 'asn_id' - res['ship_to_code'] = self.ship_to_code or '' - res['ship_not_before_date'] = self.trading_partner_id and self.ship_not_before_date or False - res['cancel_after_date'] = self.trading_partner_id and self.cancel_after_date or False - res['po_number'] = self.client_order_ref or '' - - return res - - @api.multi + @api.multi def test_edi_status(self): - for line in self.order_line: - - if line.edi_yes and not line.edi_line_msg and line.ack_yes: + if line.edi_yes and not line.edi_line_msg and line.ack_yes: return False - return True - + @api.multi def _prepare_invoice(self): """ - Prepare the dict of values to create the new invoice for a sales order. This method may be - overridden to implement custom invoice generation (making sure to call super() to establish - a clean extension chain). + Prepare the dict of values to create the new invoice for a sales order. + This method may be overridden to implement custom invoice generation + (making sure to call super() to establish a clean extension chain). """ - stock_picking_obj = self.pool['stock.picking'] - pick = False - - for pick in self.picking_ids: - - if pick.picking_type_id.id == 2: - + res = super(SaleOrder, self)._prepare_invoice() + for picking_id in self.picking_ids: + if picking_id.picking_type_id.id == 2: break - - res = super(SaleOrder, self)._prepare_invoice() - res['sale_id'] = self.id or False - res['bol_num'] = self.picking_ids and pick and pick.bol_num or self.bol_num or '' - res['picking_ids'] = self.picking_ids - res['scac_code'] = self.picking_ids and pick and pick.carrier_id.scac_code or self.scac_code or False - res['tracking_num'] = self.picking_ids and pick and pick.tracking_number or '' - res['sender_id'] = self.partner_id.sender_id or '' - res['asn_shipment'] = pick and pick.name or '' - res['trading_partner_id'] = self.trading_partner_id.id or False - res['edi_yes'] = self.edi_yes or False - res['ship_not_before_date'] = self.ship_not_before_date or False - res['cancel_after_date'] = self.cancel_after_date or False - res['supplier_code'] = self.supplier_code or False - res['ship_to_code'] = self.ship_to_code or False - res['client_order_ref'] = self.client_order_ref or False - res['tset_purpose_code'] = self.tset_purpose_code or '' - res['purchase_order_type_code'] = self.purchase_order_type_code or '' - res['po_type_description'] = self.po_type_description or '' - res['ship_complete_code'] = self.ship_complete_code or '' - res['department'] = self.department or '' - res['division'] = self.division or '' - res['promotion_deal_number'] = self.promotion_deal_number or '' - res['terms_type'] = self.terms_type or '' - res['terms_basis_date_code'] = self.terms_basis_date_code or '' - res['terms_discount_percentage'] = self.terms_discount_percentage or '' - res['terms_discount_due_days'] = self.terms_discount_due_days or '' - res['terms_net_due_days'] = self.terms_net_due_days or '' - res['payment_method_code'] = self.payment_method_code or '' - res['fob_pay_code'] = self.fob_pay_code or '' - res['fob_location_qualifier'] = self.fob_location_qualifier or '' - res['fob_location_description'] = self.fob_location_description or '' - res['fob_title_passage_code'] = self.fob_title_passage_code or '' - res['fob_title_passage_location'] = self.fob_title_passage_location or '' - res['carrier_trans_method_code'] = self.carrier_trans_method_code or '' - res['carrier_alpha_code'] = self.carrier_alpha_code or '' - res['carrier_routing'] = self.carrier_routing or '' - res['routing_sequence_code'] = self.routing_sequence_code or '' - res['service_level_code'] = self.service_level_code or '' - res['reference_qual'] = self.reference_qual or '' - res['reference_id'] = self.reference_id or '' - res['ref_description'] = self.ref_description or '' - res['note_code'] = self.note_code or '' - res['note_information_field'] = self.note_information_field or '' - res['allow_chrg_indicator'] = self.allow_chrg_indicator or '' - res['allow_chrg_code'] = self.allow_chrg_code or '' - res['allow_chrg_agency_code'] = self.allow_chrg_agency_code or '' - res['allow_chrg_agency'] = self.allow_chrg_agency or '' - res['allow_chrg_amt'] = float(self.allow_chrg_amt) or 0 - res['allow_chrg_percent_qual'] = self.allow_chrg_percent_qual or '' - res['allow_chrg_percent'] = float(self.allow_chrg_percent) or 0 - res['allow_chrg_handling_code'] = self.allow_chrg_handling_code or '' - res['reference_identification'] = self.reference_identification or '' - res['allow_chrg_handling_description'] = self.allow_chrg_handling_description or '' + + res['sale_id'] = self.id or False + res['bol_num'] =\ + self.picking_ids and picking_id and picking_id.bol_num or\ + self.bol_num or '' + res['picking_ids'] = self.picking_ids + res['scac_code'] = self.picking_ids and picking_id and\ + picking_id.carrier_id.scac_code or self.scac_code or False + res['tracking_num'] = self.picking_ids and picking_id and\ + picking_id.tracking_number or '' + res['sender_id'] = self.partner_id.sender_id or '' + res['asn_shipment'] = picking_id and picking_id.name or '' + res['trading_partner_id'] = self.trading_partner_id.id or False + res['edi_yes'] = self.edi_yes or False + res['ship_not_before_date'] = self.ship_not_before_date or False + res['cancel_after_date'] = self.cancel_after_date or False + res['supplier_code'] = self.supplier_code or False + res['ship_to_code'] = self.ship_to_code or False + res['client_order_ref'] = self.client_order_ref or False + res['tset_purpose_code'] = self.tset_purpose_code or '' + res['purchase_order_type_code'] =\ + self.purchase_order_type_code or '' + res['po_type_description'] = self.po_type_description or '' + res['ship_complete_code'] = self.ship_complete_code or '' + res['department'] = self.department or '' + res['division'] = self.division or '' + res['promotion_deal_number'] = self.promotion_deal_number or '' + res['terms_type'] = self.terms_type or '' + res['terms_basis_date_code'] = self.terms_basis_date_code or '' + res['terms_discount_percentage'] =\ + self.terms_discount_percentage or '' + res['terms_discount_due_days'] = self.terms_discount_due_days or '' + res['terms_net_due_days'] = self.terms_net_due_days or '' + res['payment_method_code'] = self.payment_method_code or '' + res['fob_pay_code'] = self.fob_pay_code or '' + res['fob_location_qualifier'] = self.fob_location_qualifier or '' + res['fob_location_description'] =\ + self.fob_location_description or '' + res['fob_title_passage_code'] = self.fob_title_passage_code or '' + res['fob_title_passage_location'] =\ + self.fob_title_passage_location or '' + res['carrier_trans_method_code'] =\ + self.carrier_trans_method_code or '' + res['carrier_alpha_code'] = self.carrier_alpha_code or '' + res['carrier_routing'] = self.carrier_routing or '' + res['routing_sequence_code'] = self.routing_sequence_code or '' + res['service_level_code'] = self.service_level_code or '' + res['reference_qual'] = self.reference_qual or '' + res['reference_id'] = self.reference_id or '' + res['ref_description'] = self.ref_description or '' + res['note_code'] = self.note_code or '' + res['note_information_field'] = self.note_information_field or '' + res['allow_chrg_indicator'] = self.allow_chrg_indicator or '' + res['allow_chrg_code'] = self.allow_chrg_code or '' + res['allow_chrg_agency_code'] = self.allow_chrg_agency_code or '' + res['allow_chrg_agency'] = self.allow_chrg_agency or '' + res['allow_chrg_amt'] = float(self.allow_chrg_amt) or 0 + res['allow_chrg_percent_qual'] = self.allow_chrg_percent_qual or '' + res['allow_chrg_percent'] = float(self.allow_chrg_percent) or 0 + res['allow_chrg_handling_code'] =\ + self.allow_chrg_handling_code or '' + res['reference_identification'] =\ + self.reference_identification or '' + res['allow_chrg_handling_description'] =\ + self.allow_chrg_handling_description or '' return res -class SaleOrderLine(osv.osv): +class SaleOrderLine(models.Model): _inherit = "sale.order.line" + @api.multi + def _prepare_procurement_values(self, group_id=False): + res = super(SaleOrderLine, self).\ + _prepare_procurement_values(group_id) + res['po_number'] = self.po_number or '' + res['asn_shipment'] = self.asn_shipment or '' + res['sale_line_id'] = self.id or False + res['edi_line_num'] = self.edi_line_num or 0 + res['so_id'] = self.order_id.id or False + res['ship_to_code'] = self.order_id.ship_to_code or '' + res['trading_partner_id'] = self.order_id.trading_partner_id and\ + self.order_id.trading_partner_id.id or False + res['edi_yes'] = self.order_id.trading_partner_id and\ + self.order_id.edi_yes or False + res['ship_not_before_date'] = self.order_id.trading_partner_id and\ + self.order_id.ship_not_before_date or False + res['cancel_after_date'] = self.order_id.trading_partner_id and\ + self.order_id.cancel_after_date or False + return res + @api.multi def _prepare_invoice_line(self, qty): """ - Prepare the dict of values to create the new invoice line for a sales order line. + Prepare the dict of values to create the new invoice line for a + sales order line. :param qty: float quantity to invoice """ - res = super(SaleOrderLine, self)._prepare_invoice_line(qty=qty) + res = super(SaleOrderLine, self)._prepare_invoice_line(qty=qty) res['edi_line_qty'] = self.edi_line_qty res['edi_line_num'] = self.edi_line_num res['asn_shipment'] = self.asn_shipment @@ -523,14 +865,16 @@ def _prepare_invoice_line(self, qty): res['product_color_code'] = self.product_color_code or '' res['product_color_description'] = self.product_color_description or '' res['product_material_code'] = self.product_material_code or '' - res['product_material_description'] = self.product_material_description or '' + res['product_material_description'] =\ + self.product_material_description or '' res['department'] = self.department or '' res['classs'] = self.classs or '' res['price_type_id_code'] = self.price_type_id_code or '' res['multiple_price_quantity'] = self.multiple_price_quantity or '' res['class_of_trade_code'] = self.class_of_trade_code or '' res['item_description_type'] = self.item_description_type or '' - res['product_characteristic_code'] = self.product_characteristic_code or '' + res['product_characteristic_code'] =\ + self.product_characteristic_code or '' res['agency_qualifier_code'] = self.agency_qualifier_code or '' res['product_description_code'] = self.product_description_code or '' res['pack_qualifier'] = self.pack_qualifier or '' @@ -553,81 +897,170 @@ def _prepare_invoice_line(self, qty): res['allow_chrg_rate'] = self.allow_chrg_rate or '' res['allow_chrg_handling_code'] = self.allow_chrg_handling_code or '' res['allow_chrg_qty_uom'] = self.allow_chrg_qty_uom or '' - res['allow_chrg_handling_description'] = self.allow_chrg_handling_description or '' + res['allow_chrg_handling_description'] =\ + self.allow_chrg_handling_description or '' return res - - def edi_line_status_change(self, cr, uid, ids, order_id, edi_line_msg, order_edi_last_date, context=None): - res = {'value': {'state':'draft', 'edi_est_ship_date': order_edi_last_date}} - if ids: + + @api.multi + def edi_line_status_change(self, order_id, edi_line_msg, + order_edi_last_date): + res = { + 'value': { + 'state': 'draft', + 'edi_est_ship_date': order_edi_last_date + } + } + if self.ids: if edi_line_msg == 'reject': res['value']['state'] = 'cancel' res['value']['edi_est_ship_date'] = '' return res - _columns = { - 'edi_yes': fields.boolean('From an EDI PO?', help="Is this order from an EDI purchase order, 850 EDI doc?"), - 'asn_shipment': fields.char('ASN Shipment Number from Converted 856'), - 'po_number': fields.char('Line Item PO Number from Converted 856'), - 'buyer_part_number': fields.char(string='Buyer Part Number'), - 'edi_line_num': fields.integer('EDI PO line number'), - 'edi_line_qty': fields.float('Original EDI Quantity', digits_compute=dp.get_precision('Product Unit of Measure')), - 'ack_yes': fields.boolean('855', readonly=True, help="Will this order have an 855?"), - 'edi_est_del_date': fields.date('Est. Del. Date', help="Line Item Estimated Delivery Date"), - 'edi_est_ship_date': fields.date('Est. Ship Date', help="Line Item Estimated Shipping Date"), - 'edi_line_msg': fields.selection((('reject', 'Reject'), ('accept', 'Accept'), ('backorder', 'Backorder')), 'EDI Line Status'), - 'ship_not_before_date': fields.date('Do Not Ship Before This Date', help="Do Not Ship Before This Date."), - 'cancel_after_date': fields.date('Cancel if Shipped After This Date', help="Cancel if Shipped After This Date."), - 'trading_partner_id': fields.many2one('edi.config', 'Trading Partner', help='EDI Configuration information for partner'), - 'edi_intransit_qty': fields.float('Incoming Qty', digits_compute=dp.get_precision('Product Unit of Measure')), - 'edi_outgoing_qty': fields.float('Reserved Qty', digits_compute=dp.get_precision('Product Unit of Measure')), - 'edi_avlforsale_qty': fields.float('Available for Sale', digits_compute=dp.get_precision('Product Unit of Measure')), - 'vendor_part_number': fields.char('VendorPartNumber'), - 'consumer_package_code': fields.char('ConsumerPackageCode'), - 'gtin': fields.char('GTIN'), - 'upc_case_code': fields.char('UPCCaseCode'), - 'purchase_price_basis': fields.char('PurchasePriceBasis'), - 'product_size_code': fields.char('ProductSizeCode'), - 'product_size_description': fields.char('ProductSizeDescription'), - 'product_color_code': fields.char('ProductColorCode'), - 'product_color_description': fields.char('ProductColorDescription'), - 'product_material_code': fields.char('ProductMaterialCode'), - 'product_material_description': fields.char('ProductMaterialDescription'), - 'department': fields.char('Department'), - 'classs': fields.char('Class'), - 'price_type_id_code': fields.char('PriceTypeIDCode'), - 'multiple_price_quantity': fields.float('MultiplePriceQuantity'), - 'class_of_trade_code': fields.char('ClassOfTradeCode'), - 'item_description_type': fields.char('ItemDescriptionType'), - 'product_characteristic_code': fields.char('ProductCharacteristicCode'), - 'agency_qualifier_code': fields.char('AgencyQualifierCode'), - 'product_description_code': fields.char('ProductDescriptionCode'), - 'pack_qualifier': fields.char('PackQualifier'), - 'pack_value': fields.integer('PackValue'), - 'pack_size': fields.char('PackSize'), - 'pack_uom': fields.char('PackUOM'), - 'packing_medium': fields.char('PackingMedium'), - 'packing_material': fields.char('PackingMaterial'), - 'pack_weight': fields.float('PackWeight'), - 'pack_weight_uom': fields.char('PackWeightUOM'), - 'location_code_qualifier':fields.char('LocationCodeQualifier', help="Code identifying the structure or format of the related location number(s)."), - 'location':fields.char('Location', help="For CrossDock, it's the marked for location. For MultiStore[could also be DC] ship-to location."), - 'allow_chrg_indicator':fields.char('AllowChrgIndicator', help="Code which indicates an allowance or charge for the service specified."), - 'allow_chrg_code':fields.char('AllowChrgCode', help="Code describing the type of allowance or charge for the service specified."), - 'allow_chrg_agency_code':fields.char('AllowChrgAgencyCode', help="Code identifying the agency assigning the code values."), - 'allow_chrg_agency':fields.char('AllowChrgAgency', help="Agency maintained code identifying the service, promotion, allowance, or charge."), - 'allow_chrg_amt':fields.float('AllowChrgAmt', help="Amount of the allowance or charge."), - 'allow_chrg_percent':fields.float('AllowChrgPercent', help="Percentage of allowance or charge. Percentages should be represented as real numbers[0% through 100% should be normalized to 0.0 through 100.00].."), - 'percent_dollar_basis':fields.float('PercentDollarBasis', help="."), - 'allow_chrg_rate':fields.float('AllowChrgRate', help="Amount of the allowance or charge."), - 'allow_chrg_handling_code':fields.char('AllowChrgHandlingCode', help="Code indicating method of handling for an allowance or charge.."), - 'allow_chrg_qty_uom':fields.char('AllowChrgQtyUOM', help=""), - 'allow_chrg_handling_description':fields.char('AllowChrgHandlingDescription', help="Free-form textual description of the note."), - } - - def _get_commitment_date(self, cr, uid, line, context=None): - """Compute the estimated delivery date""" - order = line.order_id[0] - order_datetime = datetime.strptime(order.date_order, DEFAULT_SERVER_DATETIME_FORMAT) - dt = order_datetime + timedelta(days=line.delay or 0.0) - est_del_date = dt.strftime(DEFAULT_SERVER_DATETIME_FORMAT) - return est_del_date + edi_yes = fields.Boolean( + 'From an EDI PO?', + help="Is this order from an EDI purchase order, 850 EDI doc?" + ) + asn_shipment = fields.Char( + 'ASN Shipment Number from Converted 856' + ) + po_number = fields.Char('Line Item PO Number from Converted 856') + buyer_part_number = fields.Char('Buyer Part Number') + edi_line_num = fields.Integer('EDI PO line number') + edi_line_qty = fields.Float( + 'Original EDI Quantity', + digits=dp.get_precision('Product Unit of Measure') + ) + ack_yes = fields.Boolean( + '855', + readonly=True, + help="Will this order have an 855?" + ) + edi_est_del_date = fields.Date( + 'Est. Del. Date', + help="Line Item Estimated Delivery Date" + ) + edi_est_ship_date = fields.Date( + 'Est. Ship Date', + help="Line Item Estimated Shipping Date" + ) + edi_line_msg = fields.Selection( + [('reject', 'Reject'), + ('accept', 'Accept'), + ('backorder', 'Backorder')], + 'EDI Line Status' + ) + ship_not_before_date = fields.Date( + 'Do Not Ship Before This Date', + help="Do Not Ship Before This Date." + ) + cancel_after_date = fields.Date( + 'Cancel if Shipped After This Date', + help="Cancel if Shipped After This Date." + ) + trading_partner_id = fields.Many2one( + 'edi.config', + 'Trading Partner', + help='EDI Configuration information for partner' + ) + edi_intransit_qty = fields.Float( + 'Incoming Qty', + digits=dp.get_precision('Product Unit of Measure') + ) + edi_outgoing_qty = fields.Float( + 'Reserved Qty', + digits=dp.get_precision('Product Unit of Measure') + ) + edi_avlforsale_qty = fields.Float( + 'Available for Sale', + digits=dp.get_precision('Product Unit of Measure') + ) + vendor_part_number = fields.Char('Vendor Part Number') + consumer_package_code = fields.Char('Consumer Package Code') + gtin = fields.Char('GTIN') + upc_case_code = fields.Char('UPC Case Code') + purchase_price_basis = fields.Char('Purchase Price Basis') + product_size_code = fields.Char('Product Size Code') + product_size_description = fields.Char('Product Size Description') + product_color_code = fields.Char('Product Color Code') + product_color_description = fields.Char('Product Color Description') + product_material_code = fields.Char('Product Material Code') + product_material_description = fields.Char('Product Material Description') + department = fields.Char('Department') + classs = fields.Char('Class') + price_type_id_code = fields.Char('Price Type ID Code') + multiple_price_quantity = fields.Float('Multiple Price Quantity') + class_of_trade_code = fields.Char('Class Of Trade Code') + item_description_type = fields.Char('Item Description Type') + product_characteristic_code = fields.Char('Product Characteristic Code') + agency_qualifier_code = fields.Char('Agency Qualifier Code') + product_description_code = fields.Char('Product Description Code') + pack_qualifier = fields.Char('Pack Qualifier') + pack_value = fields.Integer('Pack Value') + pack_size = fields.Char('Pack Size') + pack_uom = fields.Char('Pack UOM') + packing_medium = fields.Char('Packing Medium') + packing_material = fields.Char('Packing Material') + pack_weight = fields.Float('Pack Weight') + pack_weight_uom = fields.Char('Pack Weight UOM') + location_code_qualifier = fields.Char( + 'Location Code Qualifier', + help="Code identifying the structure or format of the" + "related location number(s)." + ) + location = fields.Char( + 'Location', + help="For CrossDock, it's the marked for location." + "For MultiStore[could also be DC] ship-to location." + ) + allow_chrg_indicator = fields.Char( + 'Allow Chrg Indicator', + help="Code which indicates an allowance or" + "charge for the service specified." + ) + allow_chrg_code = fields.Char( + 'Allow Chrg Code', + help="Code describing the type of allowance or charge for" + "the service specified." + ) + allow_chrg_agency_code = fields.Char( + 'Allow Chrg Agency Code', + help="Code identifying the agency assigning the code values." + ) + allow_chrg_agency = fields.Char( + 'Allow Chrg Agency', + help="Agency maintained code identifying the service," + "promotion, allowance, or charge." + ) + allow_chrg_amt = fields.Float( + 'Allow Chrg Amt', + help="Amount of the allowance or charge." + ) + allow_chrg_percent = fields.Float( + 'Allow Chrg Percent', + help='''Percentage of allowance or charge. Percentages should be + represented as real numbers[0% through 100% should be + normalized to 0.0 through 100.00]..''' + ) + percent_dollar_basis = fields.Float('Percent Dollar Basis') + allow_chrg_rate = fields.Float( + 'Allow Chrg Rate', + help="Amount of the allowance or charge." + ) + allow_chrg_handling_code = fields.Char( + 'Allow Chrg Handling Code', + help="Code indicating method of handling for an allowance or charge.." + ) + allow_chrg_qty_uom = fields.Char('Allow Chrg Qty UOM') + allow_chrg_handling_description = fields.Char( + 'Allow Chrg Handling Description', + help="Free-form textual description of the note." + ) + + @api.multi + def _get_commitment_date(self): + for line in self: + order = line.order_id + order_datetime = datetime.strptime(order.date_order, DS) + dt = order_datetime + timedelta(days=line.delay or 0.0) + est_del_date = dt.strftime(DS) + return est_del_date diff --git a/connector_spscommerce/models/stock.py b/connector_spscommerce/models/stock.py index b4d0f81..0772032 100644 --- a/connector_spscommerce/models/stock.py +++ b/connector_spscommerce/models/stock.py @@ -1,657 +1,57 @@ -# -*- coding: utf-8 -*- # Copyright (c) Open Source Integrators # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from datetime import datetime -import re -import dicttoxml -from openerp.osv import fields, osv -from openerp.tools.misc import DEFAULT_SERVER_DATETIME_FORMAT, DEFAULT_SERVER_DATE_FORMAT +from odoo import fields, models -class product_pricelist(osv.osv): +class ProductPricelist(models.Model): _inherit = 'product.pricelist' - - def price_get_wrapper(self, cr, uid, ids, prod_id, qty, partner=None, context=None): - price = self.price_rule_get(cr, uid, ids, prod_id, qty, partner=partner, context=context)[ids][0] - return price + def price_get_wrapper(self, prod_id, qty, partner=None): + return self.price_rule_get(prod_id, qty, partner=partner) -class stock_pack_operation(osv.osv): - _inherit = "stock.pack.operation" - _columns = { - 'tracking_number': fields.char('Tracking Number', help="Tracking Number"), - 'bol': fields.char('Bill of Lading Number', help="Bill of Lading Number"), - 'package_code': fields.selection((('PLT71', 'PLT71'), ('CTN25', 'CTN25')), 'Package Code', help="Pkg Code Qualifier.", default="PLT71"), - 'po_number': fields.char('PO Number from EDI 850', help="PO Number from EDI 850."), - 'edi_line_num': fields.char('Line Number from EDI 850', help="Line Number from EDI 850."), - 'reference_qual':fields.char('ReferenceQual', help="Code specifying the type of data in the ReferenceID/ReferenceDescription."), - 'reference_id':fields.char('ReferenceID', help="Code specifying the type of data in the ReferenceID/ReferenceDescription."), - 'ref_description':fields.char('Description', help="Free-form textual description to clarify the related data elements and their content."), - 'note_code':fields.char('NoteCode', help="Code specifying the type of note."), - 'note_information_field':fields.char('NoteInformationField', help="Free-form textual description of the note."), - 'pack_qualifier':fields.char('PackQualifier', help=""), - 'pack_value':fields.char('PackValue', help=""), - 'pack_size':fields.char('PackSize', help=""), - 'pack_uom':fields.char('PackUOM', help=""), - 'packing_medium':fields.char('PackingMedium', help=""), - 'packing_material':fields.char('PackingMaterial', help=""), - } - - -class stock_quant(osv.osv): +class StockQuant(models.Model): _inherit = "stock.quant" - _columns = { - 'tracking_number': fields.char('Tracking Number', help="Tracking Number"), - 'bol': fields.char('Bill of Lading Number', help="Bill of Lading Number"), - 'package_code': fields.selection((('PLT71', 'PLT71'), ('CTN25', 'CTN25')), 'Package Code', help="Pkg Code Qualifier.", default="PLT71"), - } - - -class stock_quant_package(osv.osv): + tracking_number = fields.Char( + 'Tracking Number', + help="Tracking Number" + ) + bol = fields.Char( + 'Bill of Lading Number', + help="Bill of Lading Number" + ) + package_code = fields.Selection( + [('PLT71', 'PLT71'), ('CTN25', 'CTN25')], + 'Package Code', + help="Pkg Code Qualifier.", + default="PLT71" + ) + + +class StockQuantPackage(models.Model): _inherit = "stock.quant.package" - _columns = { - 'tracking_number': fields.char('Tracking Number', help="Tracking Number"), - 'bol': fields.char('Bill of Lading Number', help="Bill of Lading Number"), - 'package_code': fields.selection((('PLT71', 'PLT71'), ('CTN25', 'CTN25')), 'Package Code', help="Pkg Code Qualifier.", default="PLT71"), - } - - -class delivery_carrier(osv.osv): + tracking_number = fields.Char( + 'Tracking Number', + help="Tracking Number" + ) + bol = fields.Char( + 'Bill of Lading Number', + help="Bill of Lading Number" + ) + package_code = fields.Selection( + [('PLT71', 'PLT71'), ('CTN25', 'CTN25')], + 'Package Code', + help="Pkg Code Qualifier.", + default="PLT71" + ) + + +class DeliveryCarrier(models.Model): _inherit = "delivery.carrier" - _columns = { - 'scac_code': fields.char('SCAC Ship Method Code', help="Shipping carrier code."), - } - - -class stock_move(osv.osv): - _inherit = "stock.move" - - _columns = { - 'product_material_description': fields.char('ProductMaterialDescription', help="ProductMaterialDescription"), - 'consumer_package_code': fields.char('ConsumerPackageCode', help="ConsumerPackageCode"), - 'gtin': fields.char('GTIN', help="GTIN"), - 'upc_case_code': fields.char('UPCCaseCode', help="UPCCaseCode"), - 'natl_drug_code': fields.char('NatlDrugCode', help="NatlDrugCode"), - 'international_standard_book_number': fields.char('InternationalStandardBookNumber', help="InternationalStandardBookNumber"), - 'product_size_description': fields.char('ProductSizeDescription', help="ProductSizeDescription"), - 'product_color_description': fields.char('ProductColorDescription', help="ProductColorDescription"), - } - - -class stock_picking(osv.osv): - _inherit = "stock.picking" - - _columns = { - 'ship_not_before_date': fields.date('Do Not Ship Before This Date', help="Do Not Ship Before This Date."), - 'cancel_after_date': fields.date('Cancel if Shipped After This Date', help="Cancel if Shipped After This Date."), - 'client_order_ref': fields.text('Customer PO #', help="Customer PO #"), - 'edi_yes': fields.boolean('From an EDI PO', readonly=True, help="Is this order from an EDI purchase order, 850 EDI doc."), - 'est_del_date': fields.date('Estimated Delivery Date', help="Calculated based on shipping method."), - 'trading_partner_id': fields.many2one('edi.config', 'Trading Partner', help='EDI Configuration information for partner'), - '856_sent_timestamp': fields.datetime('856 Sent Date', help="The timestamp for when the 856 was sent."), - '856_check': fields.boolean('856 Created', help="A check to see if 856 has been sent."), - 'bol_num': fields.char('BoL', help="BoL Number."), - 'tracking_number': fields.char('Tracking Number', help="Tracking Number"), - 'ship_to_code': fields.char('Ship To Warehouse', help="Trading Partner Ship to location code."), - 'package_code': fields.selection((('PLT71', 'PLT71'), ('CTN25', 'CTN25')), 'Package Code', help="Pkg Code Qualifier.", default="PLT71"), - 'tset_purpose_code':fields.char('TsetPurposeCode', help="Code identifying purpose of the document.."), - 'purchase_order_type_code':fields.char('PurchaseOrderTypeCode', help="Code specifying the type of purchase order."), - 'po_type_description':fields.char('POTypeDescription', help="Free form text to describe the type of order."), - 'ship_complete_code':fields.char('ShipCompleteCode', help="Code to identify a specific requirement or agreement of sale. Should only be used to indicate if an item can be placed on backorder."), - 'department':fields.char('Department', help="Name or number identifying an area wherein merchandise is categorized within a store."), - 'division':fields.char('Division', help="Different entities belonging to the same parent company."), - 'promotion_deal_number':fields.char('PromotionDealNumber', help="Number uniquely identifying an agreement for a special offer or price."), - 'terms_type':fields.char('TermsType', help="Code identifying type of payment terms."), - 'terms_basis_date_code':fields.char('TermsBasisDateCode', help="Code identifying the beginning of the terms period."), - 'terms_discount_percentage':fields.char('TermsDiscountPercentage', help="Terms discount percentage available to the purchaser"), - 'terms_discount_due_days':fields.char('TermsDiscountDueDays', help="Number of days by which payment or invoice must be received in order to receive the discount noted."), - 'terms_net_due_days':fields.char('TermsNetDueDays', help="Number of days until total invoice amount is due[discount not applicable."), - 'payment_method_code':fields.char('PaymentMethodCode', help="Indication of the instrument of payment."), - 'fob_pay_code':fields.char('FOBPayCode', help="Code identifying payment terms for transportation charges."), - 'fob_location_qualifier':fields.char('FOBLocationQualifier', help="Code identifying type of location at which ownership of goods is transferred."), - 'fob_location_description':fields.char('FOBLocationDescription', help="Free-form textual description of the location at which ownership of goods is transferred."), - 'fob_title_passage_code':fields.char('FOBTitlePassageCode', help="Code describing the location of ownership of the goods."), - 'fob_title_passage_location':fields.char('FOBTitlePassageLocation', help="Location of ownership of the goods."), - 'carrier_trans_method_code':fields.char('CarrierTransMethodCode', help="Code specifying the method or type of transportation for the shipment."), - 'carrier_alpha_code':fields.char('CarrierAlphaCode', help="Standard Carrier Alpha Code[SCAC] - "), - 'carrier_routing':fields.char('CarrierRouting', help="Free-form description of the routing/requested routing for shipment or the originating carrier's identity."), - 'routing_sequence_code':fields.char('RoutingSequenceCode', help=""), - 'service_level_code':fields.char('ServiceLevelCode', help="Code indicating the level of transportation service or the billing service offered by the transportation carrier."), - 'reference_qual':fields.char('ReferenceQual', help="Code specifying the type of data in the ReferenceID/ReferenceDescription."), - 'reference_id':fields.char('ReferenceID', help="Code specifying the type of data in the ReferenceID/ReferenceDescription."), - 'ref_description':fields.char('Description', help="Free-form textual description to clarify the related data elements and their content."), - 'note_code':fields.char('NoteCode', help="Code specifying the type of note."), - 'note_information_field':fields.char('NoteInformationField', help="Free-form textual description of the note."), - 'allow_chrg_indicator':fields.char('AllowChrgIndicator', help="Code which indicates an allowance or charge for the service specified."), - 'allow_chrg_code':fields.char('AllowChrgCode', help="Code describing the type of allowance or charge for the service specified."), - 'allow_chrg_agency_code':fields.char('AllowChrgAgencyCode', help="Code identifying the agency assigning the code values."), - 'allow_chrg_agency':fields.char('AllowChrgAgency', help="Agency maintained code identifying the service, promotion, allowance, or charge."), - 'allow_chrg_amt':fields.float('AllowChrgAmt', help="Amount of the allowance or charge."), - 'allow_chrg_percent_qual':fields.char('AllowChrgPercentQual', help="Code indicating on what basis an allowance or charge percent is calculated.."), - 'allow_chrg_percent':fields.float('AllowChrgPercent', help="Percentage of allowance or charge. Percentages should be represented as real numbers[0% through 100% should be normalized to 0.0 through 100.00].."), - 'allow_chrg_handling_code':fields.char('AllowChrgHandlingCode', help="Code indicating method of handling for an allowance or charge.."), - 'reference_identification':fields.char('ReferenceIdentification', help=""), - 'allow_chrg_handling_description':fields.char('AllowChrgHandlingDescription', help="Free-form textual description of the note."), - 'appointment_number':fields.char('AppointmentNumber', help=""), - 'asn_structure_code':fields.char('ASNStructureCode', help=""), - 'address_type_code':fields.char('AddressTypeCode', help=""), - 'location_code_qualifier':fields.char('LocationCodeQualifier', help=""), - 'address_location_number':fields.char('AddressLocationNumber', help=""), - 'status_code':fields.char('StatusCode', help=""), - 'equipment_description_code':fields.char('EquipmentDescriptionCode', help=""), - 'carrier_equipment_initial':fields.char('CarrierEquipmentInitial', help=""), - 'carrier_equipment_number':fields.char('CarrierEquipmentNumber', help=""), - 'seal_number':fields.char('SealNumber', help=""), - 'allow_chrg_rate':fields.char('AllowChrgRate', help=""), - } - - def put_in_pack(self, cr, uid, ids, context=None): - - stock_move_obj = self.pool["stock.move"] - stock_operation_obj = self.pool["stock.pack.operation"] - stock_move_op_link_obj = self.pool['stock.move.operation.link'] - package_obj = self.pool["stock.quant.package"] - package_id = False - context = context or {} - - for pick in self.browse(cr, uid, ids, context=context): - operations = [x for x in pick.pack_operation_ids] - - for operation in operations: - - # assign edi_line_num and po_number to pack operation, move by move. - link_ids = stock_move_op_link_obj.search(cr, uid, [('operation_id', '=', operation.id)], context=context) or [] - move = False - - for link_id in link_ids: - - link = stock_move_op_link_obj.browse(cr, uid, link_id, context=context) - - if link and link.move_id: - - move = link.move_id - break - - op_write = move and link and link.operation_id and stock_operation_obj.write(cr, uid, [link.operation_id.id], {'edi_line_num': move.edi_line_num, 'po_number':move.po_number}, context=context) or False - - return super(stock_picking, self).put_in_pack(cr, uid, ids, context=context) - - def create(self, cr, uid, vals, context=None): - - context = context or {} - res = super(stock_picking, self).create(cr, uid, vals, context=context) - picking = self.browse(cr, uid, res, context=context) - - - - if picking.origin: - - sale_obj = self.pool['sale.order'] - sale_id = sale_obj.search(cr, uid, [('name', '=', picking.origin)], context=context) - - # look back to the sale order and get the edi field values and write them to the picking - if sale_id: - - sale_order = sale_obj.browse(cr, uid, sale_id[0], context=context) - data = { - 'ship_not_before_date': sale_order.ship_not_before_date, - 'cancel_after_date': sale_order.cancel_after_date, - 'client_order_ref': sale_order.client_order_ref, - 'edi_yes': sale_order.edi_yes, - 'trading_partner_id': sale_order.trading_partner_id.id, - 'bol_num': sale_order.bol_num, - # 'tracking_number': sale_order.tracking_number, - 'scac_code': sale_order.scac_code, - 'ship_to_code': sale_order.ship_to_code, - } - - picking.write(data) - - return res - - def _get_invoice_vals(self, cr, uid, key, inv_type, journal_id, move, context=None): - res = super(stock_picking, self)._get_invoice_vals(cr, uid, key, inv_type, journal_id, move, context) - - if context is None: - context = {} - - res['so_id'] = move.picking_id.sale_id or False - res['trading_partner_id'] = move.picking_id.trading_partner_id.id or False - res['edi_yes'] = move.picking_id.edi_yes or False - res['ship_not_before_date'] = move.picking_id.ship_not_before_date or False - res['cancel_after_date'] = move.picking_id.cancel_after_date or False - res['bol_num'] = move.picking_id.bol_num or '' - res['tracking_number'] = move.picking_id.carrier_tracking_ref or '' - res['scac_code'] = move.picking_id.carrier_id.scac_code or '' - res['ship_to_code'] = move.picking_id.ship_to_code or '' - return res - - def get_operations(self, cr, uid, picking_ids, context=None): - - operations_dict = {} - stock_move_op_link_obj = self.pool['stock.move.operation.link'] - operation_obj = self.pool['stock.pack.operation'] - - for picking_id in picking_ids: - - # find links between this move and pack operations - pack_op_ids = operation_obj.search(cr, uid, [('picking_id', '=', picking_id)], context=context) - operations = operation_obj.browse(cr, uid, pack_op_ids, context=context) - operations_dict[picking_id] = operations - - # loop through quants found in packages for this particular move - for operation in operations: - - if not operation.result_package_id: - continue - - # assign edi_line_num and po_number to pack operation, move by move. - link_id = operation.qty_done and stock_move_op_link_obj.search(cr, uid, [('operation_id', '=', operation.id)], context=context) or [] - move = link_id and stock_move_op_link_obj.browse(cr, uid, link_id, context=context).move_id or False - - package = operation.result_package_id - - return operations_dict - - def create_text_856(self, cr, uid, picking_ids, context=None): - - today = str(datetime.now().strftime('%Y%m%d')) or '' - processed = 0 - name = '' - # for each picking - shipments_list = {'Shipments':[]} - for picking in self.browse(cr, uid, picking_ids, context=context): - name += picking.name - - # Grab the vendor_id and trading_partner_id for EDI in the edi_config. - trading_partner_code = picking.trading_partner_id.partner_header_string - vendor_code = picking.trading_partner_id.vendor_header_string - - # ship_date - ship_date = picking.date_done - dateship_object = datetime.strptime(ship_date, DEFAULT_SERVER_DATETIME_FORMAT) - ship_date = dateship_object.date() - ship_date = str(ship_date) - - # ship_time - ship_time = picking.date_done - dateship_object = datetime.strptime(ship_time, DEFAULT_SERVER_DATETIME_FORMAT) - ship_time = dateship_object.time() - ship_time = str(ship_time) - - # Schedule_date - schedule_date = picking.est_del_date - if not schedule_date: - schedule_date = picking.max_date - dateship_object = datetime.strptime(schedule_date, DEFAULT_SERVER_DATETIME_FORMAT) - else: - dateship_object = datetime.strptime(schedule_date, DEFAULT_SERVER_DATE_FORMAT) - schedule_date = dateship_object.date() - schedule_date = str(schedule_date) - - # Schedule_time - schedule_time = picking.est_del_date - if not schedule_time: - schedule_time = picking.max_date - dateship_object = datetime.strptime(schedule_time, DEFAULT_SERVER_DATETIME_FORMAT) - else: - dateship_object = datetime.strptime(schedule_time, DEFAULT_SERVER_DATE_FORMAT) - schedule_time = dateship_object.time() - schedule_time = str(schedule_time) - - # Notice_date - notice_date = picking.ship_not_before_date - if not notice_date: - notice_date = picking.min_date - dateship_object = datetime.strptime(notice_date, DEFAULT_SERVER_DATETIME_FORMAT) - else: - dateship_object = datetime.strptime(notice_date, DEFAULT_SERVER_DATE_FORMAT) - notice_date = dateship_object.date() - notice_date = str(notice_date) - - # get total packages and weight for the entire order - total_weight = 0 - total_qty = 0 - - for move in picking.move_lines: - total_qty += move.product_qty - total_weight += move.product_id.weight * move.product_qty - - # initialize - shipment_dict = {'Shipment':{}} - # 1. Header Line - one line for the order - meta_dict = {'Meta': {'Version': '1.0'}} - header_dict = {'Header': {'ShipmentHeader': { - 'TradingPartnerId': trading_partner_code, - 'ShipmentIdentification': str(picking.name), - 'ShipmentDate':ship_date, - 'TsetPurposeCode':picking.tset_purpose_code, - 'ShipNoticeDate':notice_date, - 'ShipNoticeTime':'00:00:00', - 'ASNStructureCode':picking.asn_structure_code, - 'BillOfLadingNumber':picking.bol_num, - 'CarrierProNumber':picking.carrier_tracking_ref, - 'AppointmentNumber':picking.appointment_number, - 'CurrentScheduledDeliveryDate':schedule_date, - 'CurrentScheduledDeliveryTime':schedule_time, - }, - 'Date': { - 'DateTimeQualifier1':'945', - 'Date1':ship_date, - 'Time1':ship_time, - 'DateTimePeriod':'', - }, - 'Reference':{ - 'ReferenceQual':picking.reference_qual, - 'ReferenceID':picking.reference_id, - 'Description':picking.ref_description, - }, - 'Notes':{ - 'NoteCode':picking.note_code, - 'NoteInformationField':picking.note_information_field, - }, - 'Contact':{ - 'ContactTypeCode':'BD', - 'ContactName':picking.partner_id and picking.partner_id.name or '', - 'PrimaryPhone':picking.partner_id and picking.partner_id.phone or '', - 'PrimaryFax':picking.partner_id and picking.partner_id.fax or '', - 'PrimaryEmail':picking.partner_id and picking.partner_id.email or '', - }, - 'Address':{ - 'AddressTypeCode':picking.address_type_code, - 'LocationCodeQualifier':picking.location_code_qualifier, - 'AddressLocationNumber':picking.address_location_number, - 'AddressName':picking.partner_id.name, - 'Address1':picking.partner_id.street, - 'Address2':picking.partner_id.street2, - 'City':picking.partner_id.city, - 'State':picking.partner_id.state_id.name, - 'PostalCode':picking.partner_id.zip, - 'Country':picking.partner_id.country_id.name, - 'Contact':{ - 'ContactTypeCode':'BD', - 'ContactName':picking.partner_id and picking.partner_id.name or '', - 'PrimaryPhone':picking.partner_id and picking.partner_id.phone or '', - 'PrimaryFax':picking.partner_id and picking.partner_id.fax or '', - 'PrimaryEmail':picking.partner_id and picking.partner_id.email or '', - }, - }, - 'CarrierInformation':{ - 'StatusCode':picking.status_code, - 'CarrierTransMethodCode':picking.carrier_trans_method_code, - 'CarrierAlphaCode':picking.carrier_alpha_code, - 'CarrierRouting':picking.carrier_routing, - 'EquipmentDescriptionCode':picking.equipment_description_code, - 'CarrierEquipmentInitial':picking.carrier_equipment_initial, - 'CarrierEquipmentNumber':picking.carrier_equipment_number, - 'SealNumber':picking.seal_number, - 'RoutingSequenceCode': picking.routing_sequence_code - }, - 'QuantityAndWeight':{ - 'LadingQuantity':total_qty, - 'WeightQualifier':'G', - 'Weight':total_weight, - 'WeightUOM':'AD', - }, - 'ChargesAllowances':{ - 'AllowChrgIndicator':picking.allow_chrg_indicator, - 'AllowChrgCode':picking.allow_chrg_code, - 'AllowChrgAmt':picking.allow_chrg_amt, - 'AllowChrgPercentQual':picking.allow_chrg_percent_qual, - 'AllowChrgPercent':picking.allow_chrg_percent, - 'AllowChrgHandlingCode':picking.allow_chrg_handling_code, - 'AllowChrgHandlingDescription':picking.allow_chrg_handling_description, - 'AllowChrgRate':picking.allow_chrg_rate - }, - 'FOBRelatedInstruction':{ - 'FOBPayCode':picking.fob_pay_code, - 'FOBLocationQualifier':picking.fob_location_qualifier, - 'FOBLocationDescription':picking.fob_location_description, - }, - } - } - orderlevel_dict = {'OrderLevel': {'OrderHeader': { - 'InternalOrderNumber': picking.name, - 'InternalOrderDate': ship_date, - 'InvoiceNumber':'99999-123', - 'InvoiceDate':'', - 'PurchaseOrderNumber':picking.origin, - 'ReleaseNumber':picking.name, - 'PurchaseOrderDate':ship_date, - 'Department':picking.department, - 'Vendor':picking.vendor, - 'CustomerOrderNumber':'' - }, - 'QuantityAndWeight':{ - 'LadingQuantity':total_qty, - 'WeightQualifier':'G', - 'Weight':total_weight, - 'WeightUOM':'AD', - }, - 'CarrierInformation':{ - 'StatusCode':picking.status_code, - 'CarrierTransMethodCode':picking.carrier_trans_method_code, - 'CarrierAlphaCode':picking.carrier_alpha_code, - 'CarrierRouting':picking.carrier_routing, - 'EquipmentDescriptionCode':picking.equipment_description_code, - 'CarrierEquipmentInitial':picking.carrier_equipment_initial, - 'CarrierEquipmentNumber':picking.carrier_equipment_number, - 'SealNumber':picking.seal_number, - 'RoutingSequenceCode': picking.routing_sequence_code - }, - 'Reference':{ - 'ReferenceQual':picking.reference_qual, - 'ReferenceID':picking.reference_id, - 'Description':picking.ref_description, - }, - 'Notes':{ - 'NoteCode':picking.note_code, - 'NoteInformationField':picking.note_information_field, - }, - 'ChargesAllowances':{ - 'AllowChrgIndicator':picking.allow_chrg_indicator, - 'AllowChrgCode':picking.allow_chrg_code, - 'AllowChrgAmt':picking.allow_chrg_amt, - 'AllowChrgPercentQual':picking.allow_chrg_percent_qual, - 'AllowChrgPercent':picking.allow_chrg_percent, - 'AllowChrgHandlingCode':picking.allow_chrg_handling_code, - 'AllowChrgHandlingDescription':picking.allow_chrg_handling_description, - 'AllowChrgRate':picking.allow_chrg_rate - }, - } - } - packlevel_dict = {'PackLevel':{}, 'Itemlevel':{'ShipmentLine':{}}} - - for operation in self.get_operations(cr, uid, picking_ids, context=context): - - packlevel_dict['PackLevel'] = {'Pack':{ - 'PackLevelType':'P', - 'ShippingSerialID':'9996999', - 'CarrierPackageID':operation.package_id and operation.package_id.id or False, - }, - 'PhysicalDetails': { - 'PackQualifier':operation.pack_qualifier, - 'PackValue':operation.pack_value, - 'PackSize':operation.pack_size, - 'PackUOM':operation.pack_uom, - 'PackingMedium':operation.packing_medium, - 'PackingMaterial':operation.packing_material, - }, - 'Date': { - 'DateTimeQualifier1':'619', - 'Date1':ship_date, - 'Time1':ship_time, - }, - 'Reference':{ - 'ReferenceQual':operation.reference_qual, - 'ReferenceID':operation.reference_id, - 'Description':operation.ref_description, - }, - 'Notes':{ - 'NoteCode':operation.note_code, - 'NoteInformationField':operation.note_information_field, - }, - } - """packlevel_dict['ItemLevel']['ShipmentLine'] = { - 'LineSequenceNumber':'01', - 'BuyerPartNumber':'9999-SPS', - 'VendorPartNumber':'', - 'ConsumerPackageCode':'FW', - 'EAN':'testReferenceID', - 'GTIN':'New products only. Do not reuse packaging', - 'UPCCaseCode':'FW', - 'NatlDrugCode':'testReferenceID', - 'InternationalStandardBookNumber':'', - 'ProductID':{ - 'PartNumberQual':'STAEV', - 'PartNumber':'REPEAT LOGO PREVIOUS ORDER', - }, - 'OrderQty':'01', - 'OrderQtyUOM':'P', - 'PurchasePrice':'9999-SPS', - 'ItemStatusCode':'', - 'ShipQty':'', - 'ShipQtyUOM':'H1', - 'ProductSizeCode':'S-800', - 'ProductSizeDescription':'Small', - 'ProductColorCode':'C-999', - 'ProductColorDescription':'Fire Truck Red', - 'ProductMaterialDescription':'', - 'NRFStandardColorAndSize':{ - 'NRFColorCode':'600', - 'NRFSizeCode':'42-10651', - }, - 'PhysicalDetails':{ - 'PackQualifier':'Small', - 'PackValue':'C-999', - 'PackSize':'', - 'PackUOM':'', - }, - 'PriceInformation':{ - 'PriceTypeIDCode':'PRP', - 'UnitPrice':'5.48', - }, - 'ProductOrItemDescription':{ - 'ItemDescriptionType':'74', - 'ProductDescription':'Super comfortable', - }, - 'Date': { - 'DateTimeQualifier1':'945', - 'Date1':ship_date, - 'Time1':ship_time, - }, - 'Reference':{ - 'ReferenceQual':'PJ', - 'ReferenceID':'testReferenceID', - 'Description':'New products only. Do not reuse packaging', - }, - 'Notes':{ - 'NoteCode':'DSCSA', - 'NoteInformationField':'REPEAT LOGO PREVIOUS ORDER', - }, - 'ChargesAllowances':{ - 'AllowChrgIndicator':'C', - 'AllowChrgCode':'C310', - 'AllowChrgAmt':'85.02', - 'AllowChrgPercentQual':'4', - 'AllowChrgPercent':'5.0', - 'AllowChrgHandlingCode':'02', - 'AllowChrgHandlingDescription':'This will cover the cost of shipping', - }, - }""" - - shipment_dict.get('Shipment').update(meta_dict) - shipment_dict.get('Shipment').update(header_dict) - shipment_dict.get('Shipment').update(orderlevel_dict) - shipment_dict.get('Shipment').get('OrderLevel').update(packlevel_dict) - - # Sublines - sublines_list = {'Sublines': []} - total_lines = 0 - total_qty = 0 - total_weight = 0 - - # for each sub line - for line in picking.move_lines: - total_lines += 1 - total_qty += line.product_qty - total_weight += (line.product_id.weight * line.product_qty) - subline_dict = {'Subline':{}} - pickingline_dict = {'ShipmentLine': { - 'LineSequenceNumber':int(line.id), - 'BuyerPartNumber':line.product_id.default_code or '', - 'VendorPartNumber':line.product_id.default_code or '', - 'ConsumerPackageCode':line.consumer_package_code, - 'EAN':line.product_id.barcode, - 'GTIN':line.gtin, - 'UPCCaseCode':line.upc_case_code, - 'NatlDrugCode':line.natl_drug_code, - 'InternationalStandardBookNumber':line.international_standard_book_number, - 'ProductID':{ - 'PartNumberQual':'IT', - 'PartNumber':line.product_id.default_code, - }, - 'PurchasePrice':line.price_unit, - 'ShipQty':line.product_qty, - 'ShipQtyUOM':'P4', - 'ProductSizeDescription':line.product_size_description, - 'ProductColorDescription':line.product_color_description, - 'ProductMaterialDescription':line.product_material_description, - } - } - subline_dict.get('Subline').update(pickingline_dict) - sublines_list.get('Sublines').append(subline_dict) - - # shipment_dict.get('Shipment').get('OrderLevel').get('PackLevel').get('ItemLevel').update(sublines_list) - shipment_dict.get('Shipment').get('OrderLevel').update(sublines_list) - - # summary - summary_dict = {'Summary': { - 'TotalLineItems':total_lines or '', - 'TotalQuantity':total_qty, - } - } - shipment_dict.get('Shipment').update(summary_dict) - # update invoice - today = datetime.now().strftime("%Y-%m-%d %H:%M:%S") - picking.write({'856_sent_timestamp':today}) - shipments_list.get('Shipments').append(shipment_dict) - processed += 1 - - # convert dictionary to xml - xml = dicttoxml.dicttoxml(shipments_list, attr_type=False, root=False) - xml = xml.replace('', '').replace('', '') - xml = xml.replace('', '').replace('', '') - # Write ASN doc to text file - name = re.findall('\d+', name)[0] - filename = '856_' + today + '%s.xml' % name - filename.replace('/', '_') - fd = open(picking.trading_partner_id.out_path + filename, 'w') - fd.write(xml) - fd.close() - return processed - - def _create_856_wrapper(self, cr, uid, context=None): - - # search for invoices that are edi_yes = True and 856_sent_timestamp = False - eligible_pickings = self.search(cr, uid, [('edi_yes', '=', True), ('856_sent_timestamp', '=', False)], context=context) - return eligible_pickings and self.create_text_856(cr, uid, eligible_pickings, context=context) or False - - # done as a server action - def action_create_text_856(self, cr, uid, ids, context=None): - """ Creates and new 856, ASN and puts it into the outbox - """ - if context is None: - context = {} - - # number of orders to process - toprocess = len(ids) - - # process orders to write 856 - processed = self.create_text_856(cr, uid, ids, context=context) - - return (toprocess - processed) + scac_code = fields.Char( + 'SCAC Ship Method Code', + help="Shipping carrier code." + ) diff --git a/connector_spscommerce/models/stock_move.py b/connector_spscommerce/models/stock_move.py index f50c952..19ce72b 100644 --- a/connector_spscommerce/models/stock_move.py +++ b/connector_spscommerce/models/stock_move.py @@ -1,44 +1,134 @@ -# -*- coding: utf-8 -*- # Copyright (c) Open Source Integrators # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from openerp import api, fields, models +from odoo import api, fields, models class StockMove(models.Model): _inherit = "stock.move" - + asn_shipment = fields.Char('ASN Shipment Number from 856') po_number = fields.Char('Line Item PO Number from 856') edi_line_num = fields.Integer('EDI PO line number') - sale_line_id = fields.Many2one('sale.order.line', 'Sale Order Line', - help='Sale Order Line from Whence this' - ' Stock Move Was created') - so_id = fields.Many2one('sale.order', 'Sale Order', - help='Sale Order from when this Invoice was' - ' created') - edi_yes = fields.Boolean('From an EDI PO?', - help="Is this order from an EDI purchase order," - " 850 EDI doc.") - ship_not_before_date = fields.Date('Do Not Ship Before This Date', - help="Do Not Ship Before This Date.") - cancel_after_date = fields.Date('Cancel if Shipped After This Date', - help="Cancel if Shipped After This Date.") - trading_partner_id = fields.Many2one('edi.config', 'Trading Partner', - help='EDI Configuration information' - ' for partner') - package_code = fields.Selection((('PLT71', 'PLT71'), - ('CTN25', 'CTN25')), - 'Package Code', - help="Pkg Code Qualifier.", - default="PLT71") - - @api.cr_uid_ids_context - def _picking_assign(self, cr, uid, move_ids, context=None): - result = super(StockMove, self).\ - _picking_assign(cr, uid, move_ids, context=context) + so_id = fields.Many2one( + 'sale.order', + 'Sale Order', + help='Sale Order from when this Invoice was created' + ) + edi_yes = fields.Boolean( + 'From an EDI PO?', + help="Is this order from an EDI purchase order, 850 EDI doc." + ) + ship_not_before_date = fields.Date( + 'Do Not Ship Before This Date', + help="Do Not Ship Before This Date." + ) + cancel_after_date = fields.Date( + 'Cancel if Shipped After This Date', + help="Cancel if Shipped After This Date." + ) + trading_partner_id = fields.Many2one( + 'edi.config', + 'Trading Partner', + help='EDI Configuration information for partner' + ) + package_code = fields.Selection( + [('PLT71', 'PLT71'), + ('CTN25', 'CTN25')], + 'Package Code', + help="Pkg Code Qualifier.", + default="PLT71" + ) + product_material_description = fields.Char( + 'Product Material Description', + help="ProductMaterialDescription" + ) + consumer_package_code = fields.Char( + 'Consumer Package Code', + help="ConsumerPackageCode" + ) + gtin = fields.Char( + 'GTIN', + help="GTIN" + ) + upc_case_code = fields.Char( + 'UPC Case Code', + help="UPCCaseCode" + ) + natl_drug_code = fields.Char( + 'Natl Drug Code', + help="NatlDrugCode" + ) + international_standard_book_number = fields.Char( + 'International Standard Book Number', + help="InternationalStandardBookNumber" + ) + product_size_description = fields.Char( + 'Product Size Description', + help="ProductSizeDescription" + ) + product_color_description = fields.Char( + 'Product Color Description', + help="ProductColorDescription" + ) + + tracking_number = fields.Char( + 'Tracking Number', + help="Tracking Number" + ) + bol = fields.Char( + 'Bill of Lading Number', + help="Bill of Lading Number" + ) + package_code = fields.Selection( + [('PLT71', 'PLT71'), ('CTN25', 'CTN25')], + 'Package Code', + help="Pkg Code Qualifier.", + default="PLT71" + ) + po_number = fields.Char( + 'PO Number from EDI 850', + help="PO Number from EDI 850." + ) + edi_line_num = fields.Char( + 'Line Number from EDI 850', + help="Line Number from EDI 850." + ) + reference_qual = fields.Char( + 'Reference Qual', + help="Code specifying the type of data in the" + "ReferenceID/ReferenceDescription." + ) + reference_id = fields.Char( + 'Reference ID', + help="Code specifying the type of data in the" + "ReferenceID/ReferenceDescription." + ) + ref_description = fields.Char( + 'Description', + help="Free-form textual description to clarify the" + "related data elements and their content." + ) + note_code = fields.Char( + 'Note Code', + help="Code specifying the type of note." + ) + note_information_field = fields.Char( + 'Note Information Field', + help="Free-form textual description of the note." + ) + pack_qualifier = fields.Char('Pack Qualifier') + pack_value = fields.Char('Pack Value') + pack_size = fields.Char('Pack Size') + pack_uom = fields.Char('Pack UOM') + packing_medium = fields.Char('Packing Medium') + packing_material = fields.Char('Packing Material') + + @api.multi + def _assign_picking(self): + result = super(StockMove, self)._assign_picking() pick_ids = {} - - for move in self.browse(cr, uid, move_ids, context=context): + + for move in self: picking = move.picking_id if not picking or pick_ids.get(picking.id, False): continue @@ -50,7 +140,7 @@ def _picking_assign(self, cr, uid, move_ids, context=None): move.procurement_id.trading_partner_id and\ move.procurement_id.trading_partner_id.id or False res['edi_yes'] = move.procurement_id.trading_partner_id\ - and edi_yes or False + and edi_yes or False res['ship_not_before_date'] = \ move.procurement_id.trading_partner_id and\ move.procurement_id.ship_not_before_date or False diff --git a/connector_spscommerce/models/stock_picking.py b/connector_spscommerce/models/stock_picking.py new file mode 100644 index 0000000..b6a913b --- /dev/null +++ b/connector_spscommerce/models/stock_picking.py @@ -0,0 +1,760 @@ +# Copyright (c) Open Source Integrators +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +import re +import dicttoxml +from datetime import datetime +from odoo import api, fields, models +from odoo.tools import DEFAULT_SERVER_DATETIME_FORMAT as DSD +from odoo.tools import DEFAULT_SERVER_DATE_FORMAT as DS +from odoo.exceptions import UserError + + +class StockPicking(models.Model): + _inherit = "stock.picking" + + ship_not_before_date = fields.Date( + 'Do Not Ship Before This Date', + help="Do Not Ship Before This Date." + ) + cancel_after_date = fields.Date( + 'Cancel if Shipped After This Date', + help="Cancel if Shipped After This Date." + ) + client_order_ref = fields.Text( + 'Customer PO #', + help="Customer PO #" + ) + edi_yes = fields.Boolean( + 'From an EDI PO', + readonly=True, + help="Is this order from an EDI purchase order, 850 EDI doc." + ) + est_del_date = fields.Date( + 'Estimated Delivery Date', + help="Calculated based on shipping method." + ) + trading_partner_id = fields.Many2one( + 'edi.config', + 'Trading Partner', + help='EDI Configuration information for partner' + ) + sent_timestamp = fields.Datetime( + '856 Sent Date', + help="The timestamp for when the 856 was sent." + ) + check = fields.Boolean( + '856 Created', + help="A check to see if 856 has been sent." + ) + bol_num = fields.Char( + 'BoL', + help="BoL Number." + ) + tracking_number = fields.Char( + 'Tracking Number', + help="Tracking Number" + ) + ship_to_code = fields.Char( + 'Ship To Warehouse', + help="Trading Partner Ship to location code." + ) + package_code = fields.Selection( + [('PLT71', 'PLT71'), ('CTN25', 'CTN25')], + 'Package Code', + help="Pkg Code Qualifier.", + default="PLT71" + ) + tset_purpose_code = fields.Char( + 'Tset Purpose Code', + help="Code identifying purpose of the document.." + ) + purchase_order_type_code = fields.Char( + 'Purchase Order TypeCode', + help="Code specifying the type of purchase order." + ) + po_type_description = fields.Char( + 'PO Type Description', + help="Free form text to describe the type of order." + ) + ship_complete_code = fields.Char( + 'Ship Complete Code', + help='''Code to identify a specific requirement or agreement of sale. + Should only be used to indicate if an item can be + placed on backorder.''' + ) + department = fields.Char( + 'Department', + help="Name or number identifying an area wherein merchandise is" + "categorized within a store." + ) + division = fields.Char( + 'Division', + help="Different entities belonging to the same parent company." + ) + promotion_deal_number = fields.Char( + 'Promotion Deal Number', + help="Number uniquely identifying an agreement for" + "a special offer or price." + ) + terms_type = fields.Char( + 'Terms Type', + help="Code identifying type of payment terms." + ) + terms_basis_date_code = fields.Char( + 'Terms Basis Date Code', + help="Code identifying the beginning of the terms period." + ) + terms_discount_percentage = fields.Char( + 'Terms Discount Percentage', + help="Terms discount percentage available to the purchaser" + ) + terms_discount_due_days = fields.Char( + 'Terms Discount Due Days', + help="Number of days by which payment or invoice must be received in" + "order to receive the discount noted." + ) + terms_net_due_days = fields.Char( + 'Terms Net Due Days', + help="Number of days until total invoice amount is" + "due[discount not applicable." + ) + payment_method_code = fields.Char( + 'Payment Method Code', + help="Indication of the instrument of payment." + ) + fob_pay_code = fields.Char( + 'FOB Pay Code', + help="Code identifying payment terms for transportation charges." + ) + fob_location_qualifier = fields.Char( + 'FOB Location Qualifier', + help="Code identifying type of location at which ownership of" + "goods is transferred." + ) + fob_location_description = fields.Char( + 'FOB Location Description', + help="Free-form textual description of the location at which" + "ownership of goods is transferred." + ) + fob_title_passage_code = fields.Char( + 'FOB Title Passage Code', + help="Code describing the location of ownership of the goods." + ) + fob_title_passage_location = fields.Char( + 'FOB Title Passage Location', + help="Location of ownership of the goods." + ) + carrier_trans_method_code = fields.Char( + 'Carrier Trans Method Code', + help="Code specifying the method or type of transportation" + "for the shipment." + ) + carrier_alpha_code = fields.Char( + 'Carrier Alpha Code', + help="Standard Carrier Alpha Code[SCAC] - " + ) + carrier_routing = fields.Char( + 'Carrier Routing', + help="Free-form description of the routing/requested routing for" + "shipment or the originating carrier's identity." + ) + routing_sequence_code = fields.Char('Routing Sequence Code') + service_level_code = fields.Char( + 'Service Level Code', + help="Code indicating the level of transportation service or the" + "billing service offered by the transportation carrier." + ) + reference_qual = fields.Char( + 'Reference Qual', + help="Code specifying the type of data in the" + "ReferenceID/ReferenceDescription." + ) + reference_id = fields.Char( + 'Reference ID', + help="Code specifying the type of data in the " + "ReferenceID/ReferenceDescription." + ) + ref_description = fields.Char( + 'Description', + help="Free-form textual description to clarify the related data" + "elements and their content." + ) + note_code = fields.Char( + 'Note Code', + help="Code specifying the type of note." + ) + note_information_field = fields.Char( + 'Note Information Field', + help="Free-form textual description of the note." + ) + allow_chrg_indicator = fields.Char( + 'Allow Chrg Indicator', + help="Code which indicates an allowance or charge for" + "the service specified." + ) + allow_chrg_code = fields.Char( + 'Allow Chrg Code', + help="Code describing the type of allowance or charge for the" + "service specified." + ) + allow_chrg_agency_code = fields.Char( + 'Allow Chrg Agency Code', + help="Code identifying the agency assigning the code values." + ) + allow_chrg_agency = fields.Char( + 'Allow Chrg Agency', + help="Agency maintained code identifying the service, promotion," + "allowance, or charge." + ) + allow_chrg_amt = fields.Float( + 'Allow Chrg Amt', + help="Amount of the allowance or charge." + ) + allow_chrg_percent_qual = fields.Char( + 'Allow Chrg Percent Qual', + help="Code indicating on what basis an allowance or charge percent" + "is calculated.." + ) + allow_chrg_percent = fields.Float( + 'Allow Chrg Percent', + help='''Percentage of allowance or charge. Percentages should be + represented as real numbers[0% through 100% should be normalized to + 0.0 through 100.00]..''' + ) + allow_chrg_handling_code = fields.Char( + 'Allow Chrg Handling Code', + help="Code indicating method of handling for an allowance or charge.." + ) + reference_identification = fields.Char('Reference Identification') + allow_chrg_handling_description = fields.Char( + 'Allow Chrg Handling Description', + help="Free-form textual description of the note." + ) + appointment_number = fields.Char('Appointment Number') + asn_structure_code = fields.Char('ASN Structure Code') + address_type_code = fields.Char('Address Type Code') + location_code_qualifier = fields.Char('Location Code Qualifier') + address_location_number = fields.Char('Address Location Number') + status_code = fields.Char('Status Code') + equipment_description_code = fields.Char('Equipment Description Code') + carrier_equipment_initial = fields.Char('Carrier Equipment Initial') + carrier_equipment_number = fields.Char('Carrier Equipment Number') + seal_number = fields.Char('Seal Number') + allow_chrg_rate = fields.Char('Allow Chrg Rate') + + def put_in_pack(self): + stock_move_line_obj = self.env['stock.move.line'] + for pick in self: + operations = pick.move_line_ids.\ + filtered(lambda o: o.qty_done > 0 and not o.result_package_id) + for operation in operations: + + # assign edi_line_num and po_number to + # pack operation, move by move. + link_ids = stock_move_line_obj.\ + search([('picking_id', '=', pick.id)]) or [] + move = False + for link_id in link_ids: + if link_id and link_id.move_id: + move = link_id.move_id + break + op_write = move and link_id and link_id.operation_id and\ + link_id.move_id.write({ + 'edi_line_num': move.edi_line_num, + 'po_number': move.po_number + }) or False + return super(StockPicking, self).put_in_pack() + + @api.model + def create(self, vals): + res = super(StockPicking, self).create(vals) + if res.origin: + sale_obj = self.env['sale.order'] + sale_id = sale_obj.search([('name', '=', res.origin)]) + + # look back to the sale order and get the edi field values and + # write them to the picking + if sale_id: + data = { + 'ship_not_before_date': sale_id.ship_not_before_date, + 'cancel_after_date': sale_id.cancel_after_date, + 'client_order_ref': sale_id.client_order_ref, + 'edi_yes': sale_id.edi_yes, + 'trading_partner_id': sale_id.trading_partner_id.id, + 'bol_num': sale_id.bol_num, + # 'tracking_number': sale_id.tracking_number, + 'scac_code': sale_id.scac_code, + 'ship_to_code': sale_id.ship_to_code, + } + res.write(data) + return res + +# def _get_invoice_vals(self, key, inv_type, journal_id, move): +# res = super(StockPicking, self +# )._get_invoice_vals(key, inv_type, journal_id, move) +# res['so_id'] = move.picking_id.sale_id or False +# res['trading_partner_id'] =\ +# move.picking_id.trading_partner_id.id or False +# res['edi_yes'] = move.picking_id.edi_yes or False +# res['ship_not_before_date'] =\ +# move.picking_id.ship_not_before_date or False +# res['cancel_after_date'] = move.picking_id.cancel_after_date or False +# res['bol_num'] = move.picking_id.bol_num or '' +# res['tracking_number'] = move.picking_id.carrier_tracking_ref or '' +# res['scac_code'] = move.picking_id.carrier_id.scac_code or '' +# res['ship_to_code'] = move.picking_id.ship_to_code or '' +# return res + + @api.multi + def get_operations(self): + operations_dict = [] + for move in self.move_lines: + if move.picking_id: + operations_dict.append(move or False) + for line in move.move_line_ids: + if not line.result_package_id: + continue + package = line.result_package_id + return operations_dict + + @api.multi + def create_text_856(self): + today = str(datetime.now().strftime(DS)) or '' + processed = 0 + name = '' + + # for each picking + shipments_list = {'Shipments': []} + for picking in self: + name += picking.name + + # Grab the vendor_id and trading_partner_id for + # EDI in the edi_config. + trading_partner_code =\ + picking.trading_partner_id.partner_header_string + vendor_code = picking.trading_partner_id.vendor_header_string + + # ship_date + if picking.scheduled_date: + ship_date = picking.scheduled_date + dateship_object = datetime.strptime(ship_date, DSD) + ship_date = dateship_object.date() + ship_date = str(ship_date) + + # ship_time + ship_time = picking.scheduled_date + dateship_object = datetime.strptime(ship_time, DSD) + ship_time = dateship_object.time() + ship_time = str(ship_time) + + # Schedule_date + schedule_date = picking.est_del_date + if not schedule_date: + schedule_date = picking.scheduled_date + dateship_object = datetime.strptime(schedule_date, DSD) + else: + dateship_object = datetime.strptime(schedule_date, DS) + schedule_date = dateship_object.date() + schedule_date = str(schedule_date) + + # Schedule_time + schedule_time = picking.est_del_date + if not schedule_time: + schedule_time = picking.scheduled_date + dateship_object = datetime.strptime(schedule_time, DSD) + else: + dateship_object = datetime.strptime(schedule_time, DS) + schedule_time = dateship_object.time() + schedule_time = str(schedule_time) + + # Notice_date + notice_date = picking.ship_not_before_date + if not notice_date: + notice_date = picking.date + dateship_object = datetime.strptime(notice_date, DSD) + else: + dateship_object = datetime.strptime(notice_date, DS) + notice_date = dateship_object.date() + notice_date = str(notice_date) + + # get total packages and weight for the entire order + total_weight = 0 + total_qty = 0 + + for move in picking.move_lines: + total_qty += move.product_qty + total_weight += move.product_id.weight * move.product_qty + + # initialize + shipment_dict = {'Shipment': {}} + # 1. Header Line - one line for the order + meta_dict = {'Meta': {'Version': '1.0'}} + header_dict = {'Header': {'ShipmentHeader': { + 'TradingPartnerId': trading_partner_code, + 'ShipmentIdentification': str(picking.name), + 'ShipmentDate': ship_date, + 'TsetPurposeCode': picking.tset_purpose_code, + 'ShipNoticeDate': notice_date, + 'ShipNoticeTime': '00:00:00', + 'ASNStructureCode': picking.asn_structure_code, + 'BillOfLadingNumber': picking.bol_num, + 'CarrierProNumber': picking.carrier_tracking_ref, + 'AppointmentNumber': picking.appointment_number, + 'CurrentScheduledDeliveryDate': schedule_date, + 'CurrentScheduledDeliveryTime': schedule_time, + }, + 'Date': { + 'DateTimeQualifier1': '945', + 'Date1': ship_date, + 'Time1': ship_time, + 'DateTimePeriod': '', + }, + 'Reference': { + 'ReferenceQual': picking.reference_qual, + 'ReferenceID': picking.reference_id, + 'Description': picking.ref_description, + }, + 'Notes': { + 'NoteCode': picking.note_code, + 'NoteInformationField': picking.note_information_field, + }, + 'Contact': { + 'ContactTypeCode': 'BD', + 'ContactName': + picking.partner_id and picking.partner_id.name or '', + 'PrimaryPhone': + picking.partner_id and picking.partner_id.phone or '', + # 'PrimaryFax': + # picking.partner_id and picking.partner_id.fax or '', + 'PrimaryEmail': + picking.partner_id and picking.partner_id.email or '', + }, + 'Address': { + 'AddressTypeCode': picking.address_type_code, + 'LocationCodeQualifier': picking.location_code_qualifier, + 'AddressLocationNumber': picking.address_location_number, + 'AddressName': picking.partner_id.name, + 'Address1': picking.partner_id.street, + 'Address2': picking.partner_id.street2, + 'City': picking.partner_id.city, + 'State': picking.partner_id.state_id.name, + 'PostalCode': picking.partner_id.zip, + 'Country': picking.partner_id.country_id.name, + 'Contact': { + 'ContactTypeCode': 'BD', + 'ContactName': + picking.partner_id and picking.partner_id.name or '', + 'PrimaryPhone': + picking.partner_id and picking.partner_id.phone or '', + # 'PrimaryFax': + # picking.partner_id and picking.partner_id.fax or '', + 'PrimaryEmail': + picking.partner_id and picking.partner_id.email or '', + }, + }, + 'CarrierInformation': { + 'StatusCode': picking.status_code, + 'CarrierTransMethodCode': + picking.carrier_trans_method_code, + 'CarrierAlphaCode': picking.carrier_alpha_code, + 'CarrierRouting': picking.carrier_routing, + 'EquipmentDescriptionCode': + picking.equipment_description_code, + 'CarrierEquipmentInitial': + picking.carrier_equipment_initial, + 'CarrierEquipmentNumber': + picking.carrier_equipment_number, + 'SealNumber': picking.seal_number, + 'RoutingSequenceCode': picking.routing_sequence_code + }, + 'QuantityAndWeight': { + 'LadingQuantity': total_qty, + 'WeightQualifier': 'G', + 'Weight': total_weight, + 'WeightUOM': 'AD', + }, + 'ChargesAllowances': { + 'AllowChrgIndicator': picking.allow_chrg_indicator, + 'AllowChrgCode': picking.allow_chrg_code, + 'AllowChrgAmt': picking.allow_chrg_amt, + 'AllowChrgPercentQual': picking.allow_chrg_percent_qual, + 'AllowChrgPercent': picking.allow_chrg_percent, + 'AllowChrgHandlingCode': picking.allow_chrg_handling_code, + 'AllowChrgHandlingDescription': + picking.allow_chrg_handling_description, + 'AllowChrgRate': picking.allow_chrg_rate + }, + 'FOBRelatedInstruction': { + 'FOBPayCode': picking.fob_pay_code, + 'FOBLocationQualifier': picking.fob_location_qualifier, + 'FOBLocationDescription': picking.fob_location_description, + }, + } + } + orderlevel_dict = {'OrderLevel': {'OrderHeader': { + 'InternalOrderNumber': picking.name, + 'InternalOrderDate': ship_date, + 'InvoiceNumber': '99999-123', + 'InvoiceDate': '', + 'PurchaseOrderNumber': picking.origin, + 'ReleaseNumber': picking.name, + 'PurchaseOrderDate': ship_date, + 'Department': picking.department, + 'Vendor': picking.partner_id.id, + 'CustomerOrderNumber': '' + }, + 'QuantityAndWeight': { + 'LadingQuantity': total_qty, + 'WeightQualifier': 'G', + 'Weight': total_weight, + 'WeightUOM': 'AD', + }, + 'CarrierInformation': { + 'StatusCode': picking.status_code, + 'CarrierTransMethodCode': + picking.carrier_trans_method_code, + 'CarrierAlphaCode': picking.carrier_alpha_code, + 'CarrierRouting': picking.carrier_routing, + 'EquipmentDescriptionCode': + picking.equipment_description_code, + 'CarrierEquipmentInitial': + picking.carrier_equipment_initial, + 'CarrierEquipmentNumber': picking.carrier_equipment_number, + 'SealNumber': picking.seal_number, + 'RoutingSequenceCode': picking.routing_sequence_code + }, + 'Reference': { + 'ReferenceQual': picking.reference_qual, + 'ReferenceID': picking.reference_id, + 'Description': picking.ref_description, + }, + 'Notes': { + 'NoteCode': picking.note_code, + 'NoteInformationField': picking.note_information_field, + }, + 'ChargesAllowances': { + 'AllowChrgIndicator': picking.allow_chrg_indicator, + 'AllowChrgCode': picking.allow_chrg_code, + 'AllowChrgAmt': picking.allow_chrg_amt, + 'AllowChrgPercentQual': picking.allow_chrg_percent_qual, + 'AllowChrgPercent': picking.allow_chrg_percent, + 'AllowChrgHandlingCode': picking.allow_chrg_handling_code, + 'AllowChrgHandlingDescription': + picking.allow_chrg_handling_description, + 'AllowChrgRate': picking.allow_chrg_rate + }, + } + } + packlevel_dict = { + 'PackLevel': {}, + 'Itemlevel': {'ShipmentLine': {}}} + + for operation in self.get_operations(): + package_id = operation.move_line_ids.\ + mapped('package_id') | operation.move_line_ids.\ + mapped('result_package_id') + packlevel_dict['PackLevel'] = {'Pack': { + 'PackLevelType': 'P', + 'ShippingSerialID': '9996999', + 'CarrierPackageID': package_id or False, + }, + 'PhysicalDetails': { + 'PackQualifier': operation.pack_qualifier, + 'PackValue': operation.pack_value, + 'PackSize': operation.pack_size, + 'PackUOM': operation.pack_uom, + 'PackingMedium': operation.packing_medium, + 'PackingMaterial': operation.packing_material, + }, + 'Date': { + 'DateTimeQualifier1': '619', + 'Date1': ship_date, + 'Time1': ship_time, + }, + 'Reference': { + 'ReferenceQual': operation.reference_qual, + 'ReferenceID': operation.reference_id, + 'Description': operation.ref_description, + }, + 'Notes': { + 'NoteCode': operation.note_code, + 'NoteInformationField': + operation.note_information_field, + }, + } + """packlevel_dict['ItemLevel']['ShipmentLine'] = { + 'LineSequenceNumber': '01', + 'BuyerPartNumber': '9999-SPS', + 'VendorPartNumber': '', + 'ConsumerPackageCode': 'FW', + 'EAN': 'testReferenceID', + 'GTIN': 'New products only. Do not reuse packaging', + 'UPCCaseCode': 'FW', + 'NatlDrugCode': 'testReferenceID', + 'InternationalStandardBookNumber': '', + 'ProductID': { + 'PartNumberQual': 'STAEV', + 'PartNumber': 'REPEAT LOGO PREVIOUS ORDER', + }, + 'OrderQty': '01', + 'OrderQtyUOM': 'P', + 'PurchasePrice': '9999-SPS', + 'ItemStatusCode': '', + 'ShipQty': '', + 'ShipQtyUOM': 'H1', + 'ProductSizeCode': 'S-800', + 'ProductSizeDescription': 'Small', + 'ProductColorCode': 'C-999', + 'ProductColorDescription': 'Fire Truck Red', + 'ProductMaterialDescription': '', + 'NRFStandardColorAndSize': { + 'NRFColorCode': '600', + 'NRFSizeCode': '42-10651', + }, + 'PhysicalDetails': { + 'PackQualifier': 'Small', + 'PackValue': 'C-999', + 'PackSize': '', + 'PackUOM': '', + }, + 'PriceInformation': { + 'PriceTypeIDCode': 'PRP', + 'UnitPrice': '5.48', + }, + 'ProductOrItemDescription': { + 'ItemDescriptionType': '74', + 'ProductDescription': 'Super comfortable', + }, + 'Date': { + 'DateTimeQualifier1': '945', + 'Date1': ship_date, + 'Time1': ship_time, + }, + 'Reference': { + 'ReferenceQual': 'PJ', + 'ReferenceID': 'testReferenceID', + 'Description': + 'New products only. Do not reuse packaging', + }, + 'Notes':{ + 'NoteCode': 'DSCSA', + 'NoteInformationField': 'REPEAT LOGO PREVIOUS ORDER', + }, + 'ChargesAllowances': { + 'AllowChrgIndicator': 'C', + 'AllowChrgCode': 'C310', + 'AllowChrgAmt': '85.02', + 'AllowChrgPercentQual': '4', + 'AllowChrgPercent': '5.0', + 'AllowChrgHandlingCode': '02', + 'AllowChrgHandlingDescription': + 'This will cover the cost of shipping', + }, + }""" + shipment_dict.get('Shipment').update(meta_dict) + shipment_dict.get('Shipment').update(header_dict) + shipment_dict.get('Shipment').update(orderlevel_dict) + shipment_dict.get('Shipment' + ).get('OrderLevel').update(packlevel_dict) + + # Sublines + sublines_list = {'Sublines': []} + total_lines = 0 + total_qty = 0 + total_weight = 0 + + # for each sub line + for line in picking.move_lines: + total_lines += 1 + total_qty += line.product_qty + total_weight += (line.product_id.weight * line.product_qty) + subline_dict = {'Subline': {}} + pickingline_dict = {'ShipmentLine': { + 'LineSequenceNumber': int(line.id), + 'BuyerPartNumber': line.product_id.default_code or '', + 'VendorPartNumber': line.product_id.default_code or '', + 'ConsumerPackageCode': line.consumer_package_code, + 'EAN': line.product_id.barcode, + 'GTIN': line.gtin, + 'UPCCaseCode': line.upc_case_code, + 'NatlDrugCode': line.natl_drug_code, + 'InternationalStandardBookNumber': + line.international_standard_book_number, + 'ProductID': { + 'PartNumberQual': 'IT', + 'PartNumber': line.product_id.default_code, + }, + 'PurchasePrice': line.price_unit, + 'ShipQty': line.product_qty, + 'ShipQtyUOM': 'P4', + 'ProductSizeDescription': line.product_size_description, + 'ProductColorDescription': line.product_color_description, + 'ProductMaterialDescription': + line.product_material_description, + } + } + subline_dict.get('Subline').update(pickingline_dict) + sublines_list.get('Sublines').append(subline_dict) + + # shipment_dict.get('Shipment').get('OrderLevel').get('PackLevel' + # ).get('ItemLevel').update(sublines_list) + shipment_dict.get('Shipment' + ).get('OrderLevel').update(sublines_list) + + # summary + summary_dict = { + 'Summary': { + 'TotalLineItems': total_lines or '', + 'TotalQuantity': total_qty, + } + } + shipment_dict.get('Shipment').update(summary_dict) + + # update invoice + today = datetime.now().strftime(DSD) + picking.write({'sent_timestamp': today}) + shipments_list.get('Shipments').append(shipment_dict) + processed += 1 + + # convert dictionary to xml + xml = dicttoxml.dicttoxml(shipments_list, attr_type=False, root=False) + xml = xml.decode("utf-8") + xml = xml.replace('', '').replace('', '') + xml = xml.replace('', '').\ + replace('', + '' + + '') + + print("***** SUCCESSFULLY STORED *****") + # Write ASN doc to text file + name = re.findall('\d+', name)[0] + filename = '856_' + today + '%s.xml' % name + filename.replace('/', '_') + if not picking.trading_partner_id.out_path: + raise UserError('Add out path in Trading Partner') + fd = open(picking.trading_partner_id.out_path + filename, + 'w') + fd.write(xml) + fd.close() + return processed + + def _create_856_wrapper(self): + # search for invoices that are edi_yes = True + # and sent_timestamp = False + eligible_pickings = self.search([ + ('edi_yes', '=', True), + ('sent_timestamp', '=', False) + ]) + return eligible_pickings and self.create_text_856() or False + + # done as a server action + @api.multi + def action_create_text_856(self): + """ Creates and new 856, ASN and puts it into the outbox + """ + # number of orders to process + toprocess = len(self.ids) + + # process orders to write 856 + processed = self.create_text_856() + return (toprocess - processed) diff --git a/connector_spscommerce/readme/CONTRIBUTORS.rst b/connector_spscommerce/readme/CONTRIBUTORS.rst index 09efd92..c0b6bfe 100644 --- a/connector_spscommerce/readme/CONTRIBUTORS.rst +++ b/connector_spscommerce/readme/CONTRIBUTORS.rst @@ -2,4 +2,6 @@ * Adam O'Connor * Sandip Mangukiya * Maxime Chambreuil +* Serpent Consulting Services Pvt. Ltd. +* Nikul Chaudhary diff --git a/connector_spscommerce/views/account_invoice_view.xml b/connector_spscommerce/views/account_invoice_view.xml index ed12086..fea585c 100644 --- a/connector_spscommerce/views/account_invoice_view.xml +++ b/connector_spscommerce/views/account_invoice_view.xml @@ -1,75 +1,76 @@ - - - - - - invoice.edi.view2 - account.invoice - form - - - -
-
-
- - - - + - - - - - - - - - - - - - - - - - - -
-
- - - - Create 810 - - code - self.action_create_text_810(cr, uid, context.get('active_ids', []), context=context) - True - + + + invoice.edi.view2 + account.invoice + form + + - - - - - Create 810 - - - - -
-
+ +