def extract(self, picking):
     """
     Takes a stock.picking browse_record and extracts the appropriate data into self.data
     @param browse_record(stock.picking) picking: the stock picking browse record object
     """
     
     self.data = OrderedDict([
         ('InboundShipmentCreate', OrderedDict([
             ('InboundShipmentHeader', OrderedDict([
                 ('ClientOfOrder', 'FW9'),
                 ('ShipmentReference', picking.name),
                 ('Warehouse', 'GAR'),
                 ('OrderType', 'PO'),
                 ('SupplierId', picking.partner_id.name),
                 ('Remark', picking.note),
                 ('DocumentFileNumber', picking.origin),
                 ('RegistrationDate', parse_date(picking.date).isoformat()),
                 ('ExpectedArrivalTime', parse_date(picking.min_date).isoformat()),
                 ('Addresses', OrderedDict([
                     ('Address', [
                     OrderedDict([
                         ('Type', 'ShipFrom'),
                         ('PartnerId', picking.partner_id.name),
                         ('Name', picking.partner_id.name),
                         ('City', picking.partner_id.city),
                         ('CityZip', picking.partner_id.zip),
                         ('District', picking.partner_id.country_id.code),
                     ])]),
                 ])),
             ])),
             ('InboundShipmentLines', OrderedDict([
                 ('InboundShipmentLine', [])                                    
             ])),
         ])),
     ])
     
     # iterate on move lines and use the template to create a data node that represents the PO line
     for move in picking.move_lines:
         
         # construct picking line dict
         picking_line = OrderedDict([
             ('LineReference', move.id),
             ('Item', OrderedDict([
                  ('ItemAttributes', OrderedDict([
                      ('Client', 'FW9'),
                      ('Item', move.product_id.ean13),                   
                  ])),
             ])),
             ('ExpectedQty', move.product_qty), 
         ])
         
         # insert data into self.data and increment picking line count
         self.data['InboundShipmentCreate']['InboundShipmentLines']['InboundShipmentLine'].append(picking_line)
         
     return self
Example #2
0
    def _extract_data(self, ret):
        """
        Extract data from a return node sent by LX1. picking_name and return_code
        are required.
        """
        assert all([field in ret for field in ['NUM_FACTURE_BL', 'CODE_MOTIF_RETOUR']]), \
            _('A return has been skipped because it was missing a required field: %s' % ret)

        ret_data = {}
        ret_data['picking_name'] = ret['NUM_FACTURE_BL']
        ret_data['return_code'] = ret['CODE_MOTIF_RETOUR']
        ret_data['return_reason'] = self.safe_get(self.return_code_mapping, ret_data['return_code']) \
                                    or _('Code not recognised: ') + ret_data['return_code']
        ret_data['return_date'] = parse_date(self.safe_get(ret, 'DATE_RETOUR'))
        ret_data['product_code'] = self.safe_get(ret, 'CODE_ART')
        ret_data['quantity_sent'] = self.safe_get(ret, 'QTEEXP')
        ret_data['quantity_returned'] = self.safe_get(ret, 'QTERET')
        return ret_data
Example #3
0
    def _extract_data(self, ret):
        """
        Extract data from a return node sent by LX1. picking_name and return_code
        are required.
        """
        assert all([field in ret for field in ["NUM_FACTURE_BL", "CODE_MOTIF_RETOUR"]]), _(
            "A return has been skipped because it was missing a required field: %s" % ret
        )

        ret_data = {}
        ret_data["picking_name"] = ret["NUM_FACTURE_BL"]
        ret_data["return_code"] = ret["CODE_MOTIF_RETOUR"]
        ret_data["return_reason"] = (
            self.safe_get(self.return_code_mapping, ret_data["return_code"])
            or _("Code not recognised: ") + ret_data["return_code"]
        )
        ret_data["return_date"] = parse_date(self.safe_get(ret, "DATE_RETOUR"))
        ret_data["product_code"] = self.safe_get(ret, "CODE_ART")
        ret_data["quantity_sent"] = self.safe_get(ret, "QTEEXP")
        ret_data["quantity_returned"] = self.safe_get(ret, "QTERET")
        return ret_data
Example #4
0
    def extract(self, picking_out):
        """
        Takes a stock.picking_out.out browse_record and extracts the
        appropriate data into self.data

        @param picking_out: browse_record(stock.picking.out)
        """
        pool = picking_out.pool
        cr = picking_out._cr
        uid = 1

        carrier_obj = pool['delivery.carrier']
        product_obj = pool['product.product']
        picking_obj = pool['stock.picking']

        picking = picking_obj.browse(cr, 1, picking_out.id)
        shipping_partner = picking_out.sale_id.partner_shipping_id
        invoice_partner = picking_out.sale_id.partner_invoice_id

        carrier = None
        carrier_name = None
        carrier_move_ids = []

        # Get carrier information from the sale_id
        if picking_out.sale_id.carrier_id:
            carrier = picking_out.sale_id.carrier_id
            carrier_name = carrier.lx_ref

        # Delivery methods can be added as move lines, so find all move lines whose products
        # are the delivery products of a delivery method and save IDS and lx ref for later
        product_ids = [
            move.product_id.id for move in picking_out.move_lines
            if move.product_id
        ]  # all products on SO
        carrier_map = product_obj.is_delivery_method(
            cr, uid, product_ids
        )  # dict of product ids with delivery method ids as values

        carrier_product_ids = [
            product_id for product_id, carrier_ids in carrier_map.iteritems()
            if carrier_ids
        ]  # IDS of products used in delivery orders
        carrier_move_ids = [
            move.id for move in picking.move_lines
            if move.product_id and move.product_id.id in carrier_product_ids
        ]  # IDS of moves for delivery order products

        # Set carrier and carrier_name if not already set
        carrier_move_ids_temp = copy(carrier_move_ids)
        while not carrier_name and len(carrier_move_ids_temp) > 0:
            carrier_move_id = carrier_move_ids_temp.pop()
            for move in picking_out.move_lines:
                if move.id in carrier_move_ids:
                    carrier = carrier_obj.browse(
                        cr, uid, carrier_map[move.product_id.id][0])
                    carrier_name = carrier.lx_ref or ''

        # Carrier LX Ref is required
        if carrier and not carrier_name:
            raise osv.except_osv(
                _("Missing Carrier LX Ref"),
                _("The delivery carrier '%s' does not have an LX Reference. Please provide one by going to Warehouse > Delivery Methods"
                  ) % carrier.name)
        if not carrier and not carrier_name:
            carrier_name = 'DHL_FW9'

        # generate invoice reports
        if not all([
                invoice.state in ['open', 'paid']
                for invoice in picking.sale_id.invoice_ids
        ]):
            raise osv.except_osv(
                _('Invoice is Draft'),
                _('Picking "%s" has an invoice in draft state. All invoices belonging to this picking must be validated before processing.'
                  ) % picking.name)

        self.add_attachments(
            pool, cr, uid, 'account.invoice',
            [invoice.id for invoice in picking.sale_id.invoice_ids],
            'account.report_invoice', picking.name + '_invoice', 'InvoiceDoc')

        # generate delivery slip
        self.add_attachments(pool, cr, uid, 'stock.picking.out', [picking.id],
                             'stock.report_picking',
                             picking.name + '_delivery', 'ShptDoc')

        # extract browse_record into self.data
        self.data = OrderedDict([
            ('DeliveryOrderCreate',
             OrderedDict([
                 ('DeliveryOrderHeader',
                  OrderedDict([
                      ('ClientOfOrder', 'FW9'),
                      ('OrderReference', picking_out.sale_id.name),
                      ('Warehouse', 'GAR'),
                      ('CustomerId', invoice_partner.id),
                      ('ShippingType', carrier_name),
                      ('ExpectedDeliveryDate',
                       parse_date(picking_out.min_date).isoformat()),
                      ('Remark', picking_out.note or ''),
                      ('DocumentFileNumber', picking_out.name),
                      ('Addresses',
                       OrderedDict([
                           ('Address', [
                               OrderedDict([
                                   ('Type', 'ShipTo'),
                                   ('PartnerId', shipping_partner.name),
                                   ('Name', shipping_partner.name),
                                   ('Street', shipping_partner.street or ''),
                                   ('City', shipping_partner.city or ''),
                                   ('CityZip', shipping_partner.zip),
                                   ('CountryCode',
                                    shipping_partner.country_id.code),
                               ]),
                               OrderedDict([
                                   ('Type', 'BillTo'),
                                   ('PartnerId', invoice_partner.name),
                                   ('Name', invoice_partner.name),
                                   ('Street', invoice_partner.street or ''),
                                   ('City', invoice_partner.city or ''),
                                   ('CityZip', invoice_partner.zip),
                                   ('CountryCode',
                                    invoice_partner.country_id.code),
                               ])
                           ]),
                       ])),
                      ('Attributes', OrderedDict([
                          ('Attribute', []),
                      ])),
                  ])),
                 ('DeliveryOrderLines', OrderedDict([('DeliveryOrderLine', [])
                                                     ])),
             ])),
        ])

        # fill attachments
        for content, name, extension, type in self._attachments:
            attachment = OrderedDict([
                ('AttributeType', type),
                ('AttributeValue', '%s.%s' % (name, extension)),
            ])
            self.data['DeliveryOrderCreate']['DeliveryOrderHeader'][
                'Attributes']['Attribute'].append(attachment)

        for move in picking_out.move_lines:

            # skip lines that are cancelled, missing product_id, or product_id is delivery method or service
            if move.state == 'cancel' \
            or not move.product_id \
            or move.id in carrier_move_ids \
            or move.product_id.type == 'service':
                continue

            # prepare line information
            line = OrderedDict([
                ('LineReference', move.id),
                ('Item',
                 OrderedDict([
                     ('ItemAttributes',
                      OrderedDict([
                          ('Client', 'FW9'),
                          ('Item', move.product_id.ean13),
                      ])),
                     ('SerialCaptureFlag', 'No'),
                     ('QuantityRoundUpRule', 'EXACT'),
                     ('InventorizedItemFlag', 'Yes'),
                 ])),
                ('OrderQty', move.product_qty),
            ])

            # add line into list of lines and increment line counter
            self.data['DeliveryOrderCreate']['DeliveryOrderLines'][
                'DeliveryOrderLine'].append(line)

        return self
    def _process_picking(self, pool, cr, picking_name, picking_lines_original):
        """
        Executes the reception wizard for an IN or the delivery wizard for an out with
        data from self.data received from LX1
        @param pool: OpenERP object pool
        @param cursor cr: OpenERP database cursor
        @param str picking_name: Name of the picking to be processed
        @param list picking_lines: A list of picking move data. Refer to self._extract_to_process
        """
        # validate params and find picking
        if not picking_lines_original:
            return
        
        assert picking_name, _("A picking was received from LX1 without a name, so we can't process it")
        picking_id = self._find_picking(pool, cr, picking_name)
        assert picking_id, _("No picking found with name %s" % picking_name)
        picking = pool.get('stock.picking').browse(cr, 1, picking_id)
        assert picking.state != 'done', _("Picking '%s' (%d) has already been closed" % (picking_name, picking_id))

        # create working copy of picking_lines_original in case original is needed intact higher in the stack
        picking_lines = deepcopy(picking_lines_original)
        
        # set move_date to first not falsey date in picking_lines, or now()
        move_date = datetime.now()
        for picking_line in picking_lines:
            if picking_line['date']:
                move_date = parse_date(picking_line['date'])
                break
            
        # create a wizard record for this picking
        context = {
            'active_model': 'stock.picking.%s' % picking_lines[0]['move_type'],
            'active_ids': [picking_id],
            'active_id': picking_id,
        }
        wizard_obj = pool.get('stock.partial.picking')
        wizard_line_obj = pool.get('stock.partial.picking.line')
        wizard_id = wizard_obj.create(cr, 1, {'date': move_date}, context=context)
        wizard = wizard_obj.browse(cr, 1, wizard_id)
        
        # reset quantities received to zero
        wizard_line_obj.write(cr, 1, [move.id for move in wizard.move_ids], {'quantity': 0})

        # Consider: Picking with two lines same product x 1 and 5. Receive 5 on 1 and 1 on 5, 4 left over to deliver!        
        # Sort wizard moves and picking lines to have lowest quantity first so they are processed in that order.
        wizard.move_ids.sort(key=lambda move: move.move_id.product_qty)
        picking_lines.sort(key=lambda line: line['quantity'], reverse=True) # reverse order because they are reverse iterated
        
        # Iterate on wizard move lines setting quantity received to that in picking lines, or 0
        for move in wizard.move_ids:
            
            move_product_reference = move.product_id.x_new_ref
            move_quantity_ordered = move.move_id.product_qty
            remainder = None
            
            # process picking lines in reverse order, removing them at the end
            for picking_line_index in reversed(xrange(len(picking_lines))):
                picking_line = picking_lines[picking_line_index]
            
                # If picking line name matches move product x_new_ref, process picking_line quantity    
                if move_product_reference == picking_line['name']:

                    # If received qty > ordered qty and we have second picking line for same product, 
                    # set received qty to full and add remainder to next picking line                    
                    if picking_line['quantity'] > move_quantity_ordered \
                        and len([line for line in picking_lines if line['name'] == move_product_reference]) > 1:
                        
                        # Set move qty as full. Save remainder for next line
                        wizard_line_obj.write(cr, 1, move.id, {'quantity': move_quantity_ordered})
                        remainder = picking_line['quantity'] - move_quantity_ordered
                    
                    else:
                        # just write quantity on line
                        wizard_line_obj.write(cr, 1, move.id, {'quantity': picking_line['quantity']})
                    
                    # picking line processed so delete it from the list                    
                    del picking_lines[picking_line_index]
                    
                    # add remainder to next line
                    if remainder != None:
                        [line for line in picking_lines if line['name'] == move_product_reference][0]['quantity'] += remainder
                        remainder = None
  
                    # break when picking line is found for move, to avoid processing multiple picking lines on a single move                  
                    break
                    
        # Process receipt
        wizard_obj.do_partial(cr, 1, [wizard_id])
Example #6
0
    def extract(self, picking):
        """
        Takes a stock.picking browse_record and extracts the appropriate data into self.data
        @param browse_record(stock.picking) picking: the stock picking browse record object
        """

        self.data = OrderedDict([
            ('InboundShipmentCreate',
             OrderedDict([
                 ('InboundShipmentHeader',
                  OrderedDict([
                      ('ClientOfOrder', 'FW9'),
                      ('ShipmentReference', picking.name),
                      ('Warehouse', 'GAR'),
                      ('OrderType', 'PO'),
                      ('SupplierId', picking.partner_id.name),
                      ('Remark', picking.note),
                      ('DocumentFileNumber', picking.origin),
                      ('RegistrationDate',
                       parse_date(picking.date).isoformat()),
                      ('ExpectedArrivalTime',
                       parse_date(picking.min_date).isoformat()),
                      ('Addresses',
                       OrderedDict([
                           ('Address', [
                               OrderedDict([
                                   ('Type', 'ShipFrom'),
                                   ('PartnerId', picking.partner_id.name),
                                   ('Name', picking.partner_id.name),
                                   ('City', picking.partner_id.city),
                                   ('CityZip', picking.partner_id.zip),
                                   ('District',
                                    picking.partner_id.country_id.code),
                               ])
                           ]),
                       ])),
                  ])),
                 ('InboundShipmentLines',
                  OrderedDict([('InboundShipmentLine', [])])),
             ])),
        ])

        # iterate on move lines and use the template to create a data node that represents the PO line
        for move in picking.move_lines:

            # construct picking line dict
            picking_line = OrderedDict([
                ('LineReference', move.id),
                ('Item',
                 OrderedDict([
                     ('ItemAttributes',
                      OrderedDict([
                          ('Client', 'FW9'),
                          ('Item', move.product_id.ean13),
                      ])),
                 ])),
                ('ExpectedQty', move.product_qty),
            ])

            # insert data into self.data and increment picking line count
            self.data['InboundShipmentCreate']['InboundShipmentLines'][
                'InboundShipmentLine'].append(picking_line)

        return self
Example #7
0
    def extract(self, picking_out):
        """
        Takes a stock.picking_out.out browse_record and extracts the
        appropriate data into self.data

        @param picking_out: browse_record(stock.picking.out)
        """
        pool = picking_out.pool
        cr = picking_out._cr
        uid = 1
        
        carrier_obj = pool['delivery.carrier']
        product_obj = pool['product.product']
        picking_obj = pool['stock.picking']
        
        picking = picking_obj.browse(cr, 1, picking_out.id)
        shipping_partner = picking_out.sale_id.partner_shipping_id
        invoice_partner = picking_out.sale_id.partner_invoice_id
        
        carrier = None
        carrier_name = None
        carrier_move_ids = []
        
        # Get carrier information from the sale_id
        if picking_out.sale_id.carrier_id:
            carrier = picking_out.sale_id.carrier_id
            carrier_name = carrier.lx_ref
        
        # Delivery methods can be added as move lines, so find all move lines whose products
        # are the delivery products of a delivery method and save IDS and lx ref for later
        product_ids = [move.product_id.id for move in picking_out.move_lines if move.product_id] # all products on SO
        carrier_map = product_obj.is_delivery_method(cr, uid, product_ids) # dict of product ids with delivery method ids as values

        carrier_product_ids = [product_id for product_id, carrier_ids in carrier_map.iteritems() if carrier_ids] # IDS of products used in delivery orders
        carrier_move_ids = [move.id for move in picking.move_lines if move.product_id and move.product_id.id in carrier_product_ids] # IDS of moves for delivery order products
        
        # Set carrier and carrier_name if not already set
        carrier_move_ids_temp = copy(carrier_move_ids)
        while not carrier_name and len(carrier_move_ids_temp) > 0:
            carrier_move_id = carrier_move_ids_temp.pop()
            for move in picking_out.move_lines:
                if move.id in carrier_move_ids:
                    carrier = carrier_obj.browse(cr, uid, carrier_map[move.product_id.id][0])
                    carrier_name = carrier.lx_ref or ''
                    
        # Carrier LX Ref is required
        if carrier and not carrier_name:
            raise osv.except_osv(_("Missing Carrier LX Ref"), _("The delivery carrier '%s' does not have an LX Reference. Please provide one by going to Warehouse > Delivery Methods") % carrier.name)
        if not carrier and not carrier_name:
            carrier_name = 'DHL_FW9'
        
        # generate invoice reports 
        if not all([invoice.state in ['open', 'paid'] for invoice in picking.sale_id.invoice_ids]):
            raise osv.except_osv(_('Invoice is Draft'), _('Picking "%s" has an invoice in draft state. All invoices belonging to this picking must be validated before processing.') % picking.name)
        
        self.add_attachments(pool, cr, uid, 'account.invoice', [invoice.id for invoice in picking.sale_id.invoice_ids],
                              'account.report_invoice', picking.name + '_invoice', 'InvoiceDoc')
        
        # generate delivery slip
        self.add_attachments(pool, cr, uid, 'stock.picking.out', [picking.id], 
                             'stock.report_picking', picking.name + '_delivery', 'ShptDoc')

        # extract browse_record into self.data        
        self.data = OrderedDict([
            ('DeliveryOrderCreate', OrderedDict([
                ('DeliveryOrderHeader', OrderedDict([
                    ('ClientOfOrder', 'FW9'),
                    ('OrderReference', picking_out.sale_id.name),
                    ('Warehouse', 'GAR'),
                    ('CustomerId', invoice_partner.id),
                    ('ShippingType', carrier_name),
                    ('ExpectedDeliveryDate', parse_date(picking_out.min_date).isoformat()),
                    ('Remark', picking_out.note or ''),
                    ('DocumentFileNumber', picking_out.name),
                    ('Addresses', OrderedDict([
                        ('Address', [OrderedDict([
                            ('Type', 'ShipTo'),
                            ('PartnerId', shipping_partner.name),
                            ('Name', shipping_partner.name),
                            ('Street', shipping_partner.street or ''),
                            ('City', shipping_partner.city or ''),
                            ('CityZip', shipping_partner.zip),
                            ('CountryCode', shipping_partner.country_id.code),
                        ]),
                        OrderedDict([
                            ('Type', 'BillTo'),
                            ('PartnerId', invoice_partner.name),
                            ('Name', invoice_partner.name),
                            ('Street', invoice_partner.street or ''),
                            ('City', invoice_partner.city or ''),
                            ('CityZip', invoice_partner.zip),
                            ('CountryCode', invoice_partner.country_id.code),
                        ])]),
                    ])),
                    ('Attributes', OrderedDict([
                        ('Attribute', []),
                    ])),
                ])),
                ('DeliveryOrderLines', OrderedDict([
                    ('DeliveryOrderLine', [])                                    
                ])),
            ])),
        ])
        
        # fill attachments
        for content, name, extension, type in self._attachments:
            attachment = OrderedDict([
                            ('AttributeType', type),
                            ('AttributeValue', '%s.%s' % (name, extension)),
                         ])
            self.data['DeliveryOrderCreate']['DeliveryOrderHeader']['Attributes']['Attribute'].append(attachment)
            
        for move in picking_out.move_lines:

            # skip lines that are cancelled, missing product_id, or product_id is delivery method or service
            if move.state == 'cancel' \
            or not move.product_id \
            or move.id in carrier_move_ids \
            or move.product_id.type == 'service':
                continue
            
            # prepare line information
            line = OrderedDict([
                ('LineReference', move.id),
                ('Item', OrderedDict([
                     ('ItemAttributes', OrderedDict([
                         ('Client', 'FW9'),
                         ('Item', move.product_id.ean13),
                     ])),
                     ('SerialCaptureFlag', 'No'),
                     ('QuantityRoundUpRule', 'EXACT'),
                     ('InventorizedItemFlag', 'Yes'),
                 ])),
                ('OrderQty', move.product_qty),
            ])
            
            # add line into list of lines and increment line counter
            self.data['DeliveryOrderCreate']['DeliveryOrderLines']['DeliveryOrderLine'].append(line)

        return self