コード例 #1
0
    def import_file(self, file_text, only_check=False):
        self.success = True
        self.errors = []
        xml = open_xml(file_text, _type='bur', print_error=self.print_errors)
        if nspath(xml, '//bur:BUR_List'):
            i = 0
            for x in nspath(xml, '//bur:BUR_List/bur:BUR'):
                i += 1
                # First, we try to check the records
                try:
                    text = xml_to_string(x)
                    self.import_file(text, only_check=True)
                except Warning as w:
                    print 'error>>>' * 5
                    print text
                    print '<<<error' * 5
                    raise Warning('Error on sub BUR file number {0}'.format(i),
                                  format_exception(w))
            for x in nspath(xml, '//bur:BUR_List/bur:BUR'):
                self.import_file(xml_to_string(x), only_check=False)
            return True

        validate_xml('bur', xml, print_error=False)
        imports = []
        product_obj = self.pool.get("product.product")
        lot_obj = self.pool.get('stock.production.lot')
        connection_obj = self.pool.get('stock.connect')
        mapping_bur_transactiontypes_obj = self.pool.get(
            'mapping_bur_transactiontypes')
        location_obj = self.pool.get('stock.location')

        # Gets the warehouse of the YellowCube.
        warehouse = connection_obj.browse(
            self.cr, self.uid, self.connection_id,
            context=self.context).warehouse_ids[0]
        # Header fields (under <GoodsReceiptHeader>)
        header = nspath(xml, "//bur:GoodsMovementsHeader")[0]

        # <BookingVoucherID> and <BookingVoucherYear>.
        # TODO: Check or save the value
        booking_voucher_id = nspath(header, "bur:BookingVoucherID")[0].text
        # TODO: Check or save the value
        booking_voucher_year = nspath(header, "bur:BookingVoucherYear")[0].text
        depositor_no = nspath(header, "bur:DepositorNo")[0].text

        self._check(warehouse, depositor_no == self.get_param('depositor_no'),
                    _('Invalid DepositorNo'))

        for article in nspath(xml, "//bur:BookingList/bur:BookingDetail"):
            partial_success = True
            element = {}

            # YCArticleNo
            element['yc_YCArticleNo'] = nspath(article,
                                               "bur:YCArticleNo")[0].text
            search_domain = [("yc_YCArticleNo", "=", element['yc_YCArticleNo'])
                             ]
            # ArticleNo
            article_no = nspath(article, "bur:ArticleNo")
            if len(article_no) > 0:
                # ArticleNo: Only set on dictionary, when needed for search (this avoids overwrite)
                element['default_code'] = article_no[0].text
                search_domain = [("default_code", "=", element['default_code'])
                                 ]
            ids = product_obj.search(self.cr,
                                     self.uid,
                                     search_domain,
                                     context=self.context)
            if len(ids) > 0:
                element['id'] = ids[0]
            else:
                element['id'] = -1
            imports.append(element)
            if not self._check(
                    warehouse,
                    len(ids) == 1,
                    _('Invalid search domain {0}').format(search_domain)):
                continue

            product = product_obj.browse(self.cr,
                                         self.uid,
                                         ids,
                                         context=self.context)[0]
            # EAN
            ean13 = nspath(article, 'bur:EAN')
            if ean13:
                element['ean13'] = ean13[0].text
                if product.ean13:
                    partial_success &= self._check(
                        product, product.ean13 == element['ean13'],
                        _('Product EAN13 {0} differs from XML EAN {1}').format(
                            product.ean13, element['ean13']))
            # BVPosNo
            # TODO: Check or save the value
            bv_pos_no = nspath(article, 'bur:BVPosNo')[0].text

            # Plant
            plant = nspath(article, 'bur:Plant')[0].text
            partial_success &= self._check(product,
                                           plant == self.get_param('plant_id'),
                                           _('Mismatching PlantID'))

            # MovePlant
            # TODO: Check or save
            move_plant = nspath(article, 'bur:MovePlant')

            # StorageLocation
            source_location = nspath(article, 'bur:StorageLocation')[0].text

            # MoveStorageLocation
            destination_location = nspath(article, "bur:MoveStorageLocation")
            if destination_location:
                destination_location = destination_location[0].text
            else:
                destination_location = False

            # TransactionType
            transaction_type = nspath(
                article, "bur:TransactionType"
            )[0].text  # Mandatory field, so won't fail.

            # We now determine the origin and destination locations based on the fields
            # StorageLocation, MoveStorageLocation, and TransactionType.
            # Have a look at task with ID=3725 for the algorithm which is copied below:
            # IF StorageLocation is recognized as a valid location in Odoo
            # AND MoveSorageLocation is recognized as a valid location in Odoo
            # THEN use those
            if location_obj.search(self.cr, self.uid, [('name', '=', source_location)], context=self.context, count=True) and \
               destination_location and \
               location_obj.search(self.cr, self.uid, [('name', '=', destination_location)], context=self.context, count=True):
                element['location'] = source_location
                element['destination'] = destination_location
            else:
                # ELSE look up the TransactionType given in the BUR in the configured TransactionType list,
                # and if found and the locations are valid, use them.
                is_mapped, mapped_origin_location, mapped_destination_location = \
                    mapping_bur_transactiontypes_obj.get_mapping(self.cr, self.uid, [], transaction_type, context=self.context)
                if is_mapped and mapped_origin_location and mapped_destination_location:
                    element['location'] = mapped_origin_location.name
                    element['destination'] = mapped_destination_location.name

                else:
                    # ELSE create an issue and stop processing the BUR. after resolving the TransactionType mapping, the import can be restarted...
                    self.success = False  # We know now that we had no success.
                    error_message = _(
                        'Error when importing BUR: StorageLocation and/or MoveStorageLocation were not defined or incorrect, AND '
                        'no correct mapping was defined for TransactionType={0}'
                    ).format(transaction_type)
                    self.post_issue(warehouse, error_message)

            # YCLot
            # TODO: check
            yc_lot = nspath(article, 'bur:YCLot')
            if yc_lot:
                element['yellowcube_lot'] = yc_lot[0].text

            # Lot
            lot = nspath(article, 'bur:Lot')
            if len(lot) > 0:
                element['lot'] = lot[0].text

                lot_id = lot_obj.search(self.cr,
                                        self.uid,
                                        [('product_id', '=', product.id),
                                         ('name', '=', element['lot'])],
                                        context=self.context)
                if not self._check(
                        product,
                        len(lot_id) <= 1,
                        _('Impossible to find a unique lot {0}'.format(
                            element['lot']))):
                    continue
                if not lot_id:
                    values = {'product_id': product.id, 'name': element['lot']}
                    production_date = nspath(article, "bur:ProductionDate")
                    if production_date:
                        values['date'] = self.str_date_to_postgres(
                            production_date[0].text)
                    lot_use_date = nspath(article, "bur:BestBeforeDate")
                    if lot_use_date:
                        values['use_date'] = self.str_date_to_postgres(
                            lot_use_date[0].text)
                    if only_check:
                        lot_id = None
                    else:
                        lot_id = [
                            lot_obj.create(self.cr,
                                           self.uid,
                                           values,
                                           context=self.context)
                        ]
                if lot_id is None and only_check:
                    lot = None
                else:
                    lot = lot_obj.browse(self.cr,
                                         self.uid,
                                         lot_id,
                                         context=self.context)[0]

            # StockType
            element['stock_type'] = nspath(article, 'bur:StockType')[0].text

            # Quantity
            element['qty_available'] = nspath(article,
                                              "bur:QuantityUOM")[0].text

            # QuantityUOM
            qty_uom = nspath(article,
                             "bur:QuantityUOM")[0].attrib['QuantityISO']
            qty_uom_ids = self.pool.get('product.uom').search(
                self.cr,
                self.uid, [('uom_iso', '=', qty_uom)],
                context=self.context)

            partial_success &= self._check(
                product, qty_uom_ids,
                _('There is not any Unit of Measure with ISO code being {0}.'.
                  format(qty_uom)))
            if partial_success:
                element['qty_uom_id'] = qty_uom_ids[0]

            write_on_lot = {}
            # BestBeforDate
            lot_use_date = nspath(article, "bur:BestBeforeDate")
            element['lot_use_date'] = False
            if len(lot_use_date) > 0:
                lot_use_date = lot_use_date[0].text
                element['lot_use_date'] = self.str_date_to_postgres(
                    lot_use_date)
                if lot is None:
                    self._check(product, only_check,
                                _('The lot may not exists in a two step file'))
                else:
                    if not lot.use_date:
                        write_on_lot['use_date'] = element['lot_use_date']
                    else:
                        partial_success &= self._check(
                            product,
                            self.keep_only_date(
                                lot.use_date) == element['lot_use_date'],
                            _('Mismatch with lot best before date'))

            # ProductionDate
            production_date = nspath(article, "bur:ProductionDate")
            element['date'] = False
            if production_date:
                lot_date = lot_use_date[0].text
                element['lot_date'] = self.str_date_to_postgres(lot_date)
                if not lot.date:
                    write_on_lot['date'] = element['lot_date']
                else:
                    partial_success &= self._check(
                        product,
                        self.keep_only_date(lot.date) == element['lot_date'],
                        _('Mismatch with lot fabrication date'))

            if write_on_lot and partial_success:
                lot.write(write_on_lot)

            element['name'] = "BUR-{0}-{1}".format(
                nspath(xml, "//bur:ControlReference/bur:Timestamp")[0].text,
                nspath(article, "bur:BVPosNo")[0].text)

        # print imports
        if not self.context.get('force_import', False):
            bad_imports = [x['yc_YCArticleNo'] for x in imports if x['id'] < 0]
            if len(bad_imports) > 0:
                raise Exception(
                    "Invalid XML Elements: {0}".format(bad_imports))

        if not self.success:
            raise Warning(
                'There where errors on the import process. See import log thread.',
                self.errors)

        if only_check:
            # Everything was OK, and it could be imported in a second step.
            return True

        stock_move_pool = self.pool.get("stock.move")
        for article in imports:
            _id = article['id']
            self.mark_record(_id, 'product.product')
            _lot = False
            if 'lot' in article:
                _lot = self.pool.get('stock.production.lot').search(
                    self.cr,
                    self.uid, [('name', '=', article['lot']),
                               ('product_id', '=', _id)],
                    context=self.context)
                if len(_lot) > 0:
                    _lot = _lot[0]
                else:
                    _lot = self.pool.get('stock.production.lot').create(
                        self.cr,
                        self.uid, {
                            'name': article['lot'],
                            'product_id': _id,
                            'date': element.get('lot_date', None),
                            'use_date': element.get('lot_use_date', None)
                        },
                        context=self.context)

            def loc(is_input, element, warehouse):
                key = 'location' if is_input else 'destination'
                if (not input) and element['stock_type'] not in ['', '0', 'F']:
                    return warehouse.lot_blocked_id.id
                if element[key] == 'YROD':
                    return warehouse.lot_input_id.id
                if element[key] == 'YAFS':
                    return warehouse.lot_stock_id.id
                return self.pool['ir.model.data'].get_object_reference(
                    self.cr, self.uid, 'stock', 'location_inventory')[1]

            stock_move_id = stock_move_pool.create(
                self.cr,
                self.uid, {
                    'name':
                    article['name'],
                    'product_id':
                    _id,
                    'location_id':
                    loc(True, article, warehouse),
                    'location_dest_id':
                    loc(False, article, warehouse),
                    'product_uom_qty' if V8 else 'product_qty':
                    article['qty_available'],
                    'product_uom':
                    article['qty_uom_id'],
                    'state':
                    'done',
                    'restrict_lot_id' if V8 else 'prodlot_id':
                    _lot,
                    'origin':
                    'YellowCube',
                    'type':
                    'internal',
                    'yc_booking_voucher_id':
                    booking_voucher_id,
                    'yc_booking_voucher_year':
                    booking_voucher_year,
                },
                context=self.context)
            self.mark_record(_id, 'product.product')

        return True
コード例 #2
0
    def import_file(self, file_text):
        self.success = True
        self.errors_list = []  # Stores the errors found.

        product_obj = self.pool.get("product.product")
        lot_obj = self.pool.get('stock.production.lot')
        lot_model = 'stock.quant' if version_info[
            0] > 7 else 'stock.report.prodlots'
        stock_report_prodlots_obj = self.pool[lot_model]

        xml = open_xml(file_text, _type='bar', print_error=self.print_errors)

        imports = []
        imported_lots = []

        stock_obj = self.pool.get("stock.change.product.qty")

        warehouse = self.pool.get('stock.connect').browse(
            self.cr, self.uid, self.connection_id,
            context=self.context).warehouse_ids[0]
        location = warehouse
        loc_stock = location.lot_stock_id.id
        loc_blocked = location.lot_blocked_id.id

        article_list = nspath(xml, "//bar:ArticleList/bar:Article")
        self._check(None, article_list, _('This BAR has no products in it.'))
        if not article_list:
            logger.debug("This is an empty BAR file")
            self.post_issue(location, _('This BAR has no products in it.'))

        for article in article_list:
            partial_success = True
            element = {'location_id': loc_stock}
            quantity_uom = float(nspath(article, "bar:QuantityUOM")[0].text)

            element['yc_YCArticleNo'] = nspath(article,
                                               "bar:YCArticleNo")[0].text
            article_no = nspath(article, "bar:ArticleNo")
            ean = nspath(article, "bar:EAN")

            # We attempt to identify the product.
            # Priority: YCArticleNo, ArticleNo(default_code), EAN(ean13).
            search_domain = []
            search_domain.append([("yc_YCArticleNo", "=",
                                   element['yc_YCArticleNo'])])

            if article_no:
                element['default_code'] = article_no[0].text
                search_domain.append([("default_code", "=",
                                       element['default_code'])])
            else:
                element['default_code'] = None

            if ean:
                element['ean13'] = ean[0].text
                search_domain.append([("ean13", "=", element['ean13'])])
            else:
                element['ean13'] = None

            # Attempts to identify the product.
            for domain in search_domain:
                product_id = product_obj.search(self.cr,
                                                self.uid,
                                                domain,
                                                context=self.context)
                if product_id:
                    break
            if quantity_uom == 0 and not product_id:
                self.post_issue(
                    location,
                    _('Product for domain {0} does not exists, and a zero stock update was received'
                      ).format(search_domain))
                continue
            # If we don't have success identifying the product, we skip this line since it's impossible to continue.
            partial_success &= self._check(
                location, product_id,
                _('There is not article for domain {0}.').format(
                    search_domain))
            if not partial_success:
                continue

            partial_success &= self._check(
                location,
                len(product_id) == 1,
                _('There is more than one article for domain {0}.').format(
                    search_domain))
            if not partial_success:
                continue

            product_id = product_id[0]
            product = product_obj.browse(self.cr, self.uid, product_id,
                                         self.context)
            element['id'] = product_id
            for k in ('ean13', 'default_code', 'yc_YCArticleNo'):
                if product[k] and element[k]:
                    self._check(
                        product, element[k] == product[k],
                        _('Mismatching product reference {0}').format(k))

            # Checks/Saves Plant
            plant = nspath(article, "bar:Plant")[0].text
            current_plant = self.get_param('plant_id', required=True)
            self._check(
                product, current_plant,
                _("Configuration parameter YC PlantID is not defined for product {0}"
                  ).format(product.name))
            self._check(
                product, current_plant and current_plant == plant,
                _('Plant does not match with the value of the configuration parameter YC PlantID for product {0}'
                  ).format(product.name))

            # Checks <StorageLocation> and <StockType>
            # Notes: Check together with StorageLocation against location_id on stock.move - alarm if wrong.
            #        If free type (' ', '0', 'F', 'U') use the StorageLocation, otherwise location YBLK.
            storage_location = nspath(article, "bar:StorageLocation")[0].text
            stock_type = nspath(article, "bar:StockType")[0].text

            # If there exists the tag <StockType>, then we follow the rules.
            location_to_use_ids = False
            if stock_type in ('X', 'S', '2', '3', '0', 'F', 'U', ' '):
                if stock_type in ('0', 'F', 'U', ' '):
                    location_to_use = storage_location
                else:
                    location_to_use = 'YBLK'
                location_to_use_ids = self.pool.get('stock.location').search(
                    self.cr,
                    self.uid, [('name', '=', location_to_use)],
                    context=self.context)
                self._check(
                    product,
                    len(location_to_use_ids) == 1,
                    _("Location '{0}' was not found in the system, or was found multiple times."
                      ).format(location_to_use))
            else:
                self._check(
                    product, False,
                    _("StockType had value '{0}', which is not allowed for product {1}"
                      ).format(stock_type, product.name))

            # Determines the lot: Lot has precedence over YCLot.
            lot_id = None
            lot_search_domain = None
            lot_to_use = False
            yc_lot = nspath(article, "bar:YCLot")
            if yc_lot:
                lot_to_use = yc_lot[0].text
                lot_search_domain = [('name', '=', lot_to_use),
                                     ('product_id', '=', product_id)]
            lot = nspath(article, "bar:Lot")
            if lot:
                lot_to_use = lot[0].text
                lot_search_domain = [('name', '=', lot_to_use),
                                     ('product_id', '=', product_id)]
            if lot_search_domain:
                lot_id = lot_obj.search(self.cr,
                                        self.uid,
                                        lot_search_domain,
                                        context=self.context)
                if len(lot_id) > 0:
                    lot_id = lot_id[0]
            # check(product, lot_search_domain is None or lot_id, _("Lot={0} does not exist.").format(lot_search_domain or '<no-lot-found>'))
            if (not lot_id) and lot_search_domain:
                lot_id = lot_obj.create(self.cr,
                                        self.uid, {
                                            'name': lot_search_domain[0][2],
                                            'product_id': product_id
                                        },
                                        context=self.context)
            self._check(
                product, (not product.track_production) or lot_id,
                _("Product {0} does not have a lot and it is required so.").
                format(product.name))

            # If we have a lot, we load it,
            # AND add it to the list of lots which need to update its last appearance in the BAR.
            if lot_id:
                lot = lot_obj.browse(self.cr, self.uid, lot_id, self.context)
                imported_lots.append(lot_id)

            # Dates: BestBeforeDate.
            best_before_date = nspath(article, "bar:BestBeforeDate")
            if best_before_date:
                best_before_date = best_before_date[0].text
                if self._check(
                        product, lot_search_domain,
                        _('BestBeforeDate defined but unknown lot to use in product {0}'
                          ).format(product.name)):
                    self._check(
                        product, (not lot.use_date)
                        or (self.keep_only_date(lot.use_date)
                            == self.str_date_to_postgres(best_before_date)),
                        _("Use date ({1}) of the lot {0} and tag BestBeforeDate ({2}) do not match for product {3}."
                          ).format(lot.name, self.keep_only_date(lot.use_date),
                                   self.str_date_to_postgres(best_before_date),
                                   product.name))
                if lot_id and not lot.use_date:
                    lot_obj.write(self.cr, self.uid, lot_id, {
                        'use_date':
                        self.str_date_to_postgres(best_before_date)
                    }, self.context)

            # Dates: ProductionDate.
            production_date = nspath(article, "bar:ProductionDate")
            if production_date:
                production_date = production_date[0].text
                self._check(
                    product, (not lot_id) or (not lot.production_date)
                    or (self.keep_only_date(lot.production_date)
                        == self.str_date_to_postgres(production_date)),
                    _("Use date ({1}) of the lot {0} and tag ProductionDate ({2}) do not match for product {3}."
                      ).format(lot.name,
                               self.keep_only_date(lot.production_date),
                               self.str_date_to_postgres(production_date),
                               product.name))
                if lot_id and not lot.date:
                    lot_obj.write(
                        self.cr, self.uid, lot_id,
                        {'date': self.str_date_to_postgres(production_date)},
                        self.context)

            # Checks the QuantityUOM and QuantityISO.
            if stock_type in ('0', 'F', ' '):  # If is free location.
                if location_to_use_ids:
                    element['location_id'] = location_to_use_ids[0]
                if lot_id and location_to_use_ids:  # We have a lot and a location: check against the quantity of the lot+location.
                    stock_report_prodlot_id = stock_report_prodlots_obj.search(
                        self.cr,
                        self.uid,
                        [('location_id', '=', location_to_use_ids[0]),
                         ('lot_id' if version_info[0] > 7 else 'prodlot_id',
                          '=', lot_id), ('product_id', '=', product_id)],
                        context=self.context)
                    partial_success &= self._check(
                        product, stock_report_prodlot_id,
                        _("No combination of location={0} and lot={1} was found in the system for product {2}."
                          ).format(location_to_use, lot_to_use, product.name))
                    if partial_success and stock_report_prodlot_id:
                        # Checks that the quantity is correct.
                        stock_report_prodlot = stock_report_prodlots_obj.browse(
                            self.cr, self.uid, stock_report_prodlot_id[0],
                            self.context)
                        if stock_report_prodlot.qty != quantity_uom:
                            self.post_issue(
                                product,
                                _("QuantityUOM does not match for the combination of location={0} and lot={1}. Scraping will be make."
                                  ).format(location_to_use, lot_to_use),
                                create=True,
                                reopen=True)

                else:  # We don't have a lot: check against the quantity of the product.
                    if product.qty_available != quantity_uom:
                        self.post_issue(
                            product,
                            _("QuantityUOM does not match the quantity indicated by the field 'product_qty' of the original product. Scraping will be make."
                              ),
                            create=True,
                            reopen=True)
            else:
                # No free stock type
                element['location_id'] = loc_blocked
            element['qty_available'] = quantity_uom

            # Checks that the QuantityUOM matches with the product.
            quantity_iso = nspath(article,
                                  "bar:QuantityUOM")[0].attrib['QuantityISO']
            uom_iso_list = self.pool.get('product.uom').search(
                self.cr,
                self.uid, [('uom_iso', '=', quantity_iso)],
                context=self.context)
            if uom_iso_list:
                element['yc_bar_uom_id'] = uom_iso_list[
                    0]  # Stores the last UOM sent with the BAR.
                uom = self.pool.get('product.uom').browse(
                    self.cr, self.uid, uom_iso_list[0], self.context)
                uom_category = self.pool.get('product.uom.categ').browse(
                    self.cr,
                    self.uid,
                    uom.category_id.id,
                    context=self.context)

                # Checks that the UOM indicated is either the same, or of the same category,
                # than that of the product.
                same_uom_than_product = (
                    uom.id == product.uom_id.id) if product.uom_id else False
                same_category_than_products_uom = (
                    product.uom_id.category_id.id
                    == uom_category.id) if product.uom_id else False
                self._check(
                    product, same_uom_than_product
                    or (not same_uom_than_product
                        and same_category_than_products_uom),
                    _("The UOM of the product {0} is not the same, and they are from different categories."
                      ).format(product.name))

                #                 self._check(product,
                #                             not(product.uom_id and (uom_name != product.uom_id.name)),
                #                             _("Unit of measure '{0}' does not match with that of the product ('{1}')").format(uom_name, product.uom_id.name))

                self._check(
                    product, product.uom_id,
                    _("Product {0} does not have a unit of measure.").format(
                        product.name))
            else:
                self._check(
                    product, False,
                    _("Unit of measure '{0}' was not found in the system.").
                    format(quantity_iso))

            if lot_id:
                element['lot'] = lot_id
            # We accept blocked location
            if True or stock_type in ('0', 'F',
                                      ' '):  # If it's free location...
                imports.append(element)

        if self.success:
            # print imports

            if not self.context.get('force_import', False):
                bad_imports = [
                    x['yc_YCArticleNo'] for x in imports if x['id'] < 0
                ]
                if len(bad_imports) > 0:
                    raise Warning(
                        "Elements not defined in openERP: {0}".format(
                            bad_imports))
            else:
                logger.error(
                    "This code is not part of YellowCube's and its use is only intended for developers: START OF THE CODE."
                )
                new_imports = [
                    x for x in imports
                    if x['id'] < 0 and x['qty_available'] != 0
                ]
                for article in new_imports:
                    imports.remove(article)
                    del article['id']
                    article['type'] = 'product'
                    _id = product_obj.create(self.cr,
                                             self.uid,
                                             article,
                                             context=self.context)
                    location_id = article['location_id']
                    del article['location_id']
                    values = {
                        'product_id': _id,
                        'location_id': location_id,
                        'new_quantity': article['qty_available']
                    }
                    if 'lot' in article:
                        values['lot_id' if version_info[0] > 7 else
                               'prodlot_id'] = article['lot']
                    _update = stock_obj.create(self.cr,
                                               self.uid,
                                               values,
                                               context=self.context)
                    self.context['active_id'] = _id
                    stock_obj.browse(
                        self.cr, self.uid, _update,
                        context=self.context).change_product_qty()
                    self.mark_record(_id, 'product.product')
                    for same in [
                            x for x in new_imports
                            if x['yc_YCArticleNo'] == article['yc_YCArticleNo']
                    ]:
                        same['id'] = _id
                logger.error(
                    "This code is not part of YellowCube's and its use is only intended for developers: FINISH OF THE CODE."
                )

            # We return by context, the lots that were updated.
            self.context['imported_lots'] = imported_lots

            # We return by context, the products that where updated
            self.context['imported_products'] = imported_products = []
            for article in imports:
                # For each article it creates a wizard and updates its stock (if the actual stock is
                # lower than that expect, the quantity is moved to the loss-things warehouse;
                # otherwise the quantity is updated).
                _id = article['id']
                imported_products.append(_id)
                if _id <= 0:
                    continue
                del article['id']
                if 'name' in article:
                    del article[
                        'name']  # OpenERP is the master, so the name <ArticleDescription> is not overwritten.
                del article[
                    'default_code']  # OpenERP is the master, so the default code <ArticleNo> is not overwritten.
                location_id = article['location_id']
                del article['location_id']
                values = {
                    'product_id': _id,
                    'location_id': location_id,
                    'new_quantity': article['qty_available']
                }
                if 'lot' in article:
                    self.mark_record(article['lot'], 'stock.production.lot')
                    values['lot_id' if version_info[0] > 7 else
                           'prodlot_id'] = article['lot']
                    del article['lot']
                product_obj.write(self.cr,
                                  self.uid,
                                  _id,
                                  article,
                                  context=self.context)
                logger.debug("Updating stock {0}".format(values))
                _update = stock_obj.create(self.cr,
                                           self.uid,
                                           values,
                                           context=self.context)
                self.context['active_id'] = _id
                stock_obj.browse(self.cr,
                                 self.uid,
                                 _update,
                                 context=self.context).change_product_qty()
                self.mark_record(_id, 'product.product')

            if 'active_ids' in self.context:
                del self.context['active_ids']

        if not self.success:
            raise Warning(
                _('Some errors where found when processing BAR file:\n -{0}').
                format('\n -'.join(self.errors_list)))

        return self.success
コード例 #3
0
    def import_file(self, file_text):
        logger.debug("Processing WBA file")
        self.success = True
        self.errors = []

        # Caches the pools.
        product_obj = self.pool.get('product.product')
        stock_obj = self.pool.get('stock.picking')
        stock_move_obj = self.pool.get('stock.move')
        warehouse_obj = self.pool.get('stock.warehouse')
        purchase_order_obj = self.pool.get('purchase.order')
        config_param_obj = self.pool.get('ir.config_parameter')
        connection_obj = self.pool.get('stock.connect')
        stock_production_lot_obj = self.pool.get('stock.production.lot')

        warehouse_id = connection_obj.browse(self.cr, self.uid, self.connection_id, context=self.context).warehouse_ids[0].id
        warehouse = warehouse_obj.browse(self.cr, self.uid, warehouse_id, context=self.context)

        xml = open_xml(file_text, _type='wba', print_error=self.print_errors)
        if nspath(xml, '//wba:WBA_List'):
            i = 0
            self.cr.execute("SAVEPOINT yellowcube_wba_xml_factory__WBAList;")
            for x in nspath(xml, '//wba:WBA_List/wba:WBA'):
                i += 1
                # First, we try to check the records
                try:
                    text = xml_to_string(x)
                    self.import_file(text)
                except Warning as w:
                    self.cr.execute("ROLLBACK TO SAVEPOINT yellowcube_wba_xml_factory__WBAList;")
                    print 'error>>>' * 5
                    print text
                    print '<<<error' * 5
                    raise Warning('Error on sub WBA file number {0}'.format(i), format_exception(w))
            self.cr.execute("RELEASE SAVEPOINT yellowcube_wba_xml_factory__WBAList;")
            return True

        validate_xml('wba', xml, print_error=False)

        imports = []

        # Gets the timestamp.
        timestamp_postgres = self.str_date_to_postgres(nspath(xml, "//wba:Timestamp")[0].text)

        # Header fields (under <GoodsReceiptHeader>)
        header = nspath(xml, "//wba:GoodsReceiptHeader")[0]

        # <BookingVoucherID> and <BookingVoucherYear>.
        booking_voucher_id = nspath(header, "wba:BookingVoucherID")[0].text
        booking_voucher_year = nspath(header, "wba:BookingVoucherYear")[0].text
        supplier_order_no = nspath(header, "wba:SupplierOrderNo")[0].text  # This is the stock.picking's ID.
        picking_in_ids = stock_obj.search(self.cr, self.uid, [('yellowcube_customer_order_no', '=', supplier_order_no),
                                                              ('state', 'not in', ['cancel', 'done']),
                                                              ], context=self.context)

        # Checks if the stock.picking exists. Otherwise, logs an issue an continues with the next one.
        self._check(warehouse, len(picking_in_ids) > 0, _("There is not any stock.picking with SupplierOrderNo (id) ={0}").format(supplier_order_no))
        if not self.success:
            raise Warning('There where some errors in the WBA file.', self.errors)

        # Gets the stock picking in associated to this purchase order.
        picking_in = stock_obj.browse(self.cr, self.uid, picking_in_ids[0], self.context)

        # <SupplierNo>.
        # We first check if the supplier has a supplier number, and if that's the case we
        # compare against it. Otherwise, we compare against the default supplier number
        # set for the connector.
        supplier_no = nspath(header, "wba:SupplierNo")[0].text
        if picking_in.partner_id.supplier and picking_in.partner_id.yc_supplier_no:
            yc_supplier_no = picking_in.partner_id.yc_supplier_no
            self._check(warehouse, yc_supplier_no == supplier_no, _("Configuration variable YC SupplierNo does not match with that of tag 'SupplierNo' on the supplier."))
        else:
            yc_supplier_no = self.get_param('supplier_no', required=True)
            self._check(warehouse, yc_supplier_no, _("Configuration variable YC SupplierNo is not defined in the system."))
            self._check(warehouse, yc_supplier_no == supplier_no, _("Configuration variable YC SupplierNo does not match with that of tag 'SupplierNo' on the connector."))

        # <SupplierOrderNo>.
        stock_picking_in_count = stock_obj.search(self.cr, self.uid, [('yellowcube_customer_order_no', '=', supplier_order_no)], context=self.context, count=True)
        self._check(warehouse, stock_picking_in_count > 0, _("Stock picking in with ID={0} does not exist in the system, thus can not be processed in the WBA.").format(supplier_order_no))

        id_table = {}
        last_posno = 0
        # Update missing values
        for line in picking_in.move_lines:
            if line.yc_posno:
                if line.yc_posno > last_posno:
                    last_posno = line.yc_posno
            id_table[line.id] = line
        for line_id in sorted([x for x in id_table]):
            line = id_table[line_id]
            if not line.yc_posno:
                last_posno += 1
                line.yc_posno = last_posno
                line.write({'yc_posno': last_posno})
        # Refresh the record
        picking_in = stock_obj.browse(self.cr, self.uid, picking_in_ids[0], self.context)

        for article in nspath(xml, "//wba:GoodsReceiptList/wba:GoodsReceiptDetail"):
            partials = {}
            partial = {}

            # <SupplierOrderPosNo>
            pos_no = int(nspath(article, "wba:SupplierOrderPosNo")[0].text)

            # Gets the stock.move associated to this line.
            move_line = None
            for line in picking_in.move_lines:
                if line.yc_posno == pos_no:
                    move_line = line
                    break

            self._check(picking_in, move_line is not None, _('Mismatch with stock picking line number {0}/{1}').format(pos_no, [x.yc_posno for x in picking_in.move_lines]))
            if not self.success:
                raise Warning('Error parsing wba file', self.errors)

            partials[move_line if V8 else "move{0}".format(move_line.id)] = partial

            partial['delivery_date'] = timestamp_postgres

            # Caches the product of the stock.move.
            product_id = move_line.product_id.id
            partial['product_id'] = product_id
            product = product_obj.browse(self.cr, self.uid, product_id, self.context)

            # <YCArticleNo>
            yc_article_no = nspath(article, "wba:YCArticleNo")[0].text
            if not product.yc_YCArticleNo:
                product_obj.write(self.cr, self.uid, product_id, {'yc_YCArticleNo': yc_article_no}, self.context)
                product.message_post(_('Product {0} with ID={1} did not have a YCArticleNo, so it was created with value {2}').format(product.name, product_id, yc_article_no))
            else:
                # If the product already had a YCArticleNo, then we check if the values match.
                self._check(warehouse, product.yc_YCArticleNo == yc_article_no, _("The 'YCArticleNo' does not match with the field 'YCArticleNo' of the product."))

            # <ArticleNo>
            article_no = nspath(article, "wba:ArticleNo")
            if article_no:
                article_no = article_no[0].text
                if not product.default_code:
                    product_obj.write(self.cr, self.uid, product_id, {'default_code': article_no}, self.context)
                    product.message_post(_('Product {0} with ID={1} did not have a default_code, so it was created with value {2}').format(product.name, product_id, article_no))
                else:
                    # If the product already has an ArticleNo (field 'default_code' in Odoo), then we check if the values match.
                    self._check(warehouse, product.default_code == article_no,
                                '{0} [{1}!={2}]'.format(_("The 'ArticleNo' does not match with the field 'default_code' of the product."), product.default_code, article_no))

            # <EAN>
            ean = nspath(article, "wba:EAN")
            if ean:
                ean = ean[0].text
                if not product.ean13:
                    product_obj.write(self.cr, self.uid, product_id, {'ean13': ean}, self.context)
                    product.message_post(_('Product {0} with ID={1} did not have an ean13 code, so it was created with value {2}').format(product.name, product_id, ean))
                else:
                    # If the product already has an EAN (field 'ean13' in Odoo) then we check if both values match.
                    self._check(warehouse, product.ean13 == ean, _("The 'EAN' does not match with the field 'ean13' of the product."))

            # <Lot>
            lot_search_domain = [('product_id', '=', product_id)]
            lot = nspath(article, 'wba:Lot')
            if lot:
                lot = lot[0].text
                lot_search_domain.append(('name', '=', lot))

            # <YCLot>
            yc_lot = nspath(article, 'wba:YCLot')
            if yc_lot:
                yc_lot = yc_lot[0].text
#                 lot_search_domain.append(('yellowcube_lot', '=', yc_lot))

            # If a lot was indicated but it does not exist in the system, create it.
            lot_ids = stock_production_lot_obj.search(self.cr, self.uid, lot_search_domain, context=self.context)
            if lot and (not lot_ids):
                lot_id_ = stock_production_lot_obj.create(self.cr, self.uid, {'name': lot,
                                                                              'yellowcube_lot': yc_lot or False,
                                                                              'product_id': product_id,
                                                                              'date': timestamp_postgres},
                                                          self.context)
                lot_ids = [lot_id_]
                lot = stock_production_lot_obj.browse(self.cr, self.uid, lot_id_, self.context)
                lot.message_post(_('Stock.production.lot {0} with ID={1} did not existed, and it was created by VoucherID {2}').format(lot.name, lot.id, booking_voucher_id),)

            lot = None
            if lot_ids:
                lot = stock_production_lot_obj.browse(self.cr, self.uid, lot_ids[0], self.context)
            # If <YCLot> exists but the stock production lot does not have it, stores it. If it has it, checks.
            if yc_lot and lot:
                if not lot.yellowcube_lot:
                    stock_production_lot_obj.write(self.cr, self.uid, lot.id, {'yellowcube_lot': yc_lot}, self.context)
                    lot.message_post(_('Stock.production.lot {0} with ID={1} did not have a yellowcube_lot, so it was set with value {2}').format(lot.name, lot.id, yc_lot))
                else:
                    self._check(warehouse, lot.yellowcube_lot == yc_lot, _("YCLot in the WBA does not match with the value of the stock.production.lot"))

            if lot_ids:
                partial['restrict_lot_id' if V8 else 'prodlot_id'] = lot_ids[0]

            if product.track_incoming:
                self._check(warehouse, lot is not None, _("The WBA file must contain a lot, otherwise the stock.move can not be updated for product {0}".format(product.name)))

            # <Plant>
            plant = nspath(article, 'wba:Plant')[0].text
            current_plant = self.get_param('plant_id', required=True)
            if current_plant:
                self._check(warehouse, current_plant == plant, _('Plant does not match with the value of the configuration parameter YC PlantID.'))
            elif not current_plant:
                self.set_param('plant_id', plant)

            #  <QuantityUOM>
            quantity_uom = float(nspath(article, "wba:QuantityUOM")[0].text)
            self._check(picking_in, move_line.product_qty >= quantity_uom, _('Product {0}: QuantityUOM is greater than that of the stock.move.').format(product.name))
            partial['product_qty'] = quantity_uom

            # <QuantityISO>
            quantity_iso = nspath(article, "wba:QuantityUOM")[0].attrib['QuantityISO']
            uom_iso_list = self.pool.get('product.uom').search(self.cr, self.uid, [('uom_iso', '=', quantity_iso)], context=self.context)
            if len(uom_iso_list) > 0 and move_line.product_uom and (quantity_iso != move_line.product_uom.uom_iso):
                self._check(picking_in, False, _('Product {0}: Attribute QuantityISO does not match the ISO code indicated of the original stock.move.').format(product.name))
            else:
                if not move_line.product_uom:
                    product_uom = uom_iso_list[0]
                    partial['product_uom'] = product_uom
                else:
                    self._check(warehouse, move_line.product_uom.uom_iso == quantity_iso, _('Product {0}: Attribute QuantityISO does not match that of the stock.move.').format(product.name))
                    partial['product_uom'] = move_line.product_uom.id

            # Checks <StorageLocation> and <StockType>
            # Notes: Check together with StorageLocation against location_id on stock.move - alarm if wrong.
            #        If free type (' ', '0', 'F') use the StorageLocation, otherwise location YBLK.
            storage_location = nspath(article, "wba:StorageLocation")[0].text
            stock_type = nspath(article, "wba:StockType")
            if move_line.location_id:
                if stock_type:
                    # If there exists the tag <StockType>, then we follow the rules.
                    stock_type = stock_type[0].text

                    if stock_type not in ('X', 'S', '2', '3', '0', 'F', ' '):
                        self._check(picking_in, False, _("Product {0}: StockType had value '{1}', which is not allowed.").format(product.name, stock_type), self.context)
                    elif stock_type in ('0', 'F', ' '):
                        if move_line.location_dest_id.name != storage_location:
                            self._check(picking_in,
                                        False,
                                        _('Product {0}: StorageLocation {1} does not match with the location indicated in the stock.move {2}.').format(product.name,
                                                                                                                                                       storage_location,
                                                                                                                                                       move_line.location_dest_id.name))
                    else:
                        if move_line.location_dest_id.name != 'YBLK':
                            self._check(picking_in, False, _("Product {0}: StorageLocation must be 'YBLK' since StockType is not a free type.").format(product.name))
                else:
                    # If <StockType> does not exist, it just checks that the values match.
                    if move_line.location_dest_id.name != storage_location:
                        self._check(picking_in,
                                    False,
                                    _('Product {0}: StorageLocation {1} does not match with the location indicated in the stock.move {2}.').format(product.name,
                                                                                                                                                   storage_location,
                                                                                                                                                   move_line.location_dest_id.name))
            else:
                self._check(picking_in, False, _('Product {0}: The stock move does not have a location_id.').format(product.name))

            # <EndOfDeliveryFlag>
            if self.success:
                end_of_delivery_flag = nspath(article, "wba:EndOfDeliveryFlag")[0].text
                complete_move_ids = []
                if V8:
                    for move in partials:
                        vals = partials[move]
                        new_move_id = stock_move_obj.split(self.cr,
                                                           self.uid,
                                                           move,
                                                           vals['product_qty'],
                                                           restrict_lot_id=vals.get('restrict_lot_id', False),
                                                           context=self.context)
                        stock_move_obj.action_done(self.cr, self.uid, [new_move_id], context=self.context)
                        complete_move_ids.append(new_move_id)
                else:
                    complete_move_ids = picking_in.do_partial(partials)

                if end_of_delivery_flag == '1':  # delivery is completed.
                    number_of_pending_moves = stock_move_obj.search(self.cr, self.uid, [('picking_id', '=', picking_in.id),
                                                                                        ('state', 'in', ('draft', 'waiting', 'confirmed', 'assigned')),
                                                                                        ], context=self.context, count=True)
                    if number_of_pending_moves > 0:
                        pass  # They don't want this alarm __for the moment__.
                        #self.post_issue(picking_in, _('Tag EndOfDeliveryFlag was set, but there exists some stock move which are not in state finish nor cancelled.'))
                    else:
                        picking_in.action_done()  # Closes the picking.
                # moves may have been deleted in the process (???)
                # So that is why we need to iterate over those which are kept.
                move_ids = stock_move_obj.search(self.cr, self.uid, [('id', 'in', complete_move_ids)], context=self.context)
                stock_move_obj.write(self.cr, self.uid, move_ids, {'yc_booking_voucher_id': booking_voucher_id,
                                                                   'yc_booking_voucher_year': booking_voucher_year,
                                                                   }, self.context)

        if self.success:
            self.mark_record(picking_in.id, 'stock.picking' if V8 else 'stock.picking.in')
            # Only confirm when received the end of delivery flag
            # picking_in.action_done()
        else:
            raise Warning('There where some errors in the WBA file', self.errors)

        return True
コード例 #4
0
    def import_file(self, file_text):

        configuration_data = self.pool.get('configuration.data').get(
            self.cr, self.uid, [])

        logger.debug("Processing WAR file")
        self.success = True
        self.errors = []

        stock_obj = self.pool.get("stock.picking")
        partner_obj = self.pool.get('res.partner')
        stock_move_obj = self.pool.get('stock.move')
        product_obj = self.pool.get('product.product')
        connection_obj = self.pool.get('stock.connect')

        # Gets the warehouse of the YellowCube.
        warehouse = connection_obj.browse(
            self.cr, self.uid, self.connection_id,
            context=self.context).warehouse_ids[0]

        xml = open_xml(file_text, _type='war', print_error=self.print_errors)
        if nspath(xml, '//warr:WAR_List'):
            i = 0
            self.cr.execute("SAVEPOINT yellowcube_war_xml_factory__WARList;")
            for x in nspath(xml, '//warr:WAR_List/warr:WAR'):
                i += 1
                # First, we try to check the records
                try:
                    text = xml_to_string(x)
                    self.import_file(text)
                except Warning as w:
                    self.cr.execute(
                        "ROLLBACK TO SAVEPOINT yellowcube_war_xml_factory__WARList;"
                    )
                    print 'error>>>' * 5
                    print text
                    print '<<<error' * 5
                    raise Warning('Error on sub WAR file number {0}'.format(i),
                                  format_exception(w))
            self.cr.execute(
                "RELEASE SAVEPOINT yellowcube_war_xml_factory__WARList;")
            return True

        validate_xml('war', xml, print_error=False)

        order_header = nspath(xml, "//warr:CustomerOrderHeader")[0]

        customer_order_no = nspath(order_header,
                                   "warr:CustomerOrderNo")[0].text
        stock_ids = stock_obj.search(
            self.cr,
            self.uid,
            [('yellowcube_customer_order_no', '=', customer_order_no),
             ('state', 'in', ['confirmed', 'assigned'])],
            context=self.context)

        # Checks if the stock.picking exists. Otherwise, logs an issue an continues with the next one.
        self._check(
            warehouse,
            len(stock_ids) > 0,
            _("There is not any stock.picking with CustomerOrderNo ={0} in state confirmed or assigned."
              ).format(customer_order_no))
        if not self.success:
            raise Warning(
                'There where some errors in the WAR file: {0}'.format(
                    '\n'.join(self.errors)))

        # Gets the stock picking out associated to this WAR.
        picking_out = stock_obj.browse(self.cr,
                                       self.uid,
                                       stock_ids,
                                       context=self.context)[0]

        # Saves BookingVoucherID and BookingVoucherYear on the stock.move
        goods_issue_header = nspath(xml, "//warr:GoodsIssueHeader")[0]
        booking_voucher_id = nspath(goods_issue_header,
                                    "warr:BookingVoucherID")[0].text
        booking_voucher_year = nspath(goods_issue_header,
                                      "warr:BookingVoucherYear")[0].text
        # TODO: Put this at the end, like in the WBA.
        #         for move_line in picking_out.move_lines:
        #             stock_move_obj.write(self.cr, self.uid, move_line.id, {'booking_voucher_id': booking_voucher_id,
        #                                                                     'booking_voucher_year': booking_voucher_year,
        #                                                                     }, self.context)

        # Validates DepositorNo against the system's parameter. If does not match, then aborts and logs an issue.
        depositor_no = nspath(goods_issue_header, "warr:DepositorNo")[0].text
        expected_depositor_no = self.get_param('depositor_no', required=True)
        self._check(
            warehouse, expected_depositor_no,
            _("Variable YC DepositorNo is not defined in the configuration data."
              ))
        self._check(
            warehouse, depositor_no == expected_depositor_no,
            _("Configuration variable YC DepositorNo does not match with that of tag 'DepositorNo'"
              ))

        # <YCDeliveryNo>
        yellowcube_delivery_no = nspath(order_header,
                                        "warr:YCDeliveryNo")[0].text
        if yellowcube_delivery_no and picking_out.yellowcube_delivery_no and picking_out.yellowcube_delivery_no != yellowcube_delivery_no:
            self.post_issue(
                warehouse,
                _('YCDeliveryNo {0} does not match its current value {1} in the stock picking.'
                  ).format(picking_out.yellowcube_delivery_no,
                           yellowcube_delivery_no),
                create=True,
                reopen=True)

        if picking_out.yellowcube_delivery_no != yellowcube_delivery_no:
            stock_obj.write(self.cr,
                            self.uid, [picking_out.id],
                            {'yellowcube_delivery_no': yellowcube_delivery_no},
                            context=self.context)

        # <YCDeloveryDate>
        yellowcube_delivery_date = nspath(order_header,
                                          "warr:YCDeliveryDate")[0].text
        if yellowcube_delivery_date and picking_out.yellowcube_delivery_date and picking_out.yellowcube_delivery_date != yellowcube_delivery_date:
            self.post_issue(
                warehouse,
                _('YCDeliveryDate {0} does not match its current value {1} in the stock picking.'
                  ).format(picking_out.yellowcube_delivery_date,
                           yellowcube_delivery_date),
                create=True,
                reopen=True)

        if picking_out.yellowcube_delivery_date != yellowcube_delivery_date:
            stock_obj.write(
                self.cr,
                self.uid, [picking_out.id],
                {'yellowcube_delivery_date': yellowcube_delivery_date},
                context=self.context)

        # <PartnerReference>
        partner_reference = nspath(order_header, "warr:PartnerReference")
        if partner_reference:
            partner_reference = partner_reference[0].text
            if picking_out.partner_id.ref:
                self._check(
                    warehouse, picking_out.partner_id.ref == partner_reference,
                    _('PartnerReference does not match its current value in the stock picking.'
                      ))
            else:
                partner_obj.write(self.cr,
                                  self.uid,
                                  picking_out.partner_id.id,
                                  {'ref': partner_reference},
                                  context=self.context)

        # <PostalShipmentNo>
        carrier_tracking_ref = nspath(order_header,
                                      "warr:PostalShipmentNo")[0].text
        stock_obj.write(self.cr,
                        self.uid, [picking_out.id],
                        {'carrier_tracking_ref': carrier_tracking_ref},
                        context=self.context)

        partials = {}
        id_table = {}
        i = 1
        for line in sorted([x.id for x in picking_out.move_lines]):
            id_table[i] = line
            i += 1

        for order_move in nspath(xml, "//warr:CustomerOrderDetail"):
            partial = {}

            pos_no = int(nspath(order_move, "warr:CustomerOrderPosNo")[0].text)

            # Gets the stock.move associated to this line.
            move_line = None
            for line in picking_out.move_lines:
                if line.id == id_table[pos_no]:
                    move_line = line
                    break

            # Checks that the line exists.
            self._check(
                picking_out, move_line is not None,
                _('CustomerOrderPosNo={0}: Mismatch with stock picking line number'
                  ).format(pos_no))
            if not self.success:
                raise Warning('Error parsing WAR file: {0}'.format('\n'.join(
                    self.errors)))

            partials[move_line if V8 else "move{0}".format(move_line.id
                                                           )] = partial

            # Caches the product of the stock.move.
            product_id = move_line.product_id.id
            partial['product_id'] = product_id
            product = product_obj.browse(self.cr, self.uid, product_id,
                                         self.context)

            # <YCArticleNo>
            yc_article_no = nspath(order_move, "warr:YCArticleNo")[0].text
            if product.yc_YCArticleNo:
                self._check(
                    picking_out, product.yc_YCArticleNo == yc_article_no,
                    _('Product {0} (id={1}): YCArticleNo does not match with YCArticleNo.'
                      ).format(product.name, product_id))
            else:
                product_obj.write(self.cr, self.uid, product_id,
                                  {'yc_YCArticleNo': yc_article_no},
                                  self.context)

            # <ArticleNo>
            article_no = nspath(order_move, "warr:ArticleNo")
            if article_no:
                article_no = article_no[0].text
                self._check(
                    picking_out, product.default_code == article_no,
                    _('Product {0} (id={1}): ArticleNo does not match with default_code.'
                      ).format(product.name, product_id))

            # <EAN>
            ean = nspath(order_move, "warr:EAN")
            if ean:
                ean = ean[0].text
                if product.ean13:
                    self._check(
                        picking_out, product.ean13 == ean,
                        _('Product {0} (id={1}): EAN does not match with ean13.'
                          ).format(product.name, product_id))
                else:
                    product_obj.write(self.cr, self.uid, product_id,
                                      {'ean13': ean}, self.context)

            # <Lot>
            lot = nspath(order_move, "warr:Lot")
            if lot:
                lot = lot[0].text

                # Searches for that lot in the system.
                lot_ids = self.pool.get('stock.production.lot').search(
                    self.cr,
                    self.uid, [('name', '=', lot),
                               ('product_id', '=', product_id)],
                    context=self.context)
                if not lot_ids:
                    self._check(
                        warehouse, False,
                        _('Lot {0} for product {1} (id={2}) does not exist in the system'
                          ).format(lot, product.name, product_id))
                elif getattr(move_line,
                             'restrict_lot_id' if V8 else 'prodlot_id'):
                    if self._check(
                            picking_out,
                            getattr(move_line, 'restrict_lot_id'
                                    if V8 else 'prodlot_id').name == lot,
                            _('Product {0} (id={1}): Lot does not match the lot indicated of the original stock.move.'
                              ).format(product_obj.name, product_id)):
                        partial['restrict_lot_id'
                                if V8 else 'prodlot_id'] = lot_ids[0]

            if product.track_outgoing:
                self._check(
                    warehouse, lot,
                    _("The WAR file must contain a lot, otherwise the stock.move can not be updated for product {0}"
                      .format(product.name)))

            # <Plant>
            plant = nspath(order_move, "warr:Plant")[0].text
            current_plant = self.get_param('plant_id', required=True)
            if current_plant:
                self._check(
                    picking_out, current_plant == plant,
                    _('Product {0} (id={1}): Plant does not match with the value of the configuration parameter YC PlantID.'
                      ).format(product.name, product_id))
            elif not current_plant:
                configuration_data.write(self.cr, self.uid,
                                         configuration_data.id,
                                         {'yc_plant_id': plant}, self.context)

            #  <QuantityUOM>
            quantity_uom = float(
                nspath(order_move, "warr:QuantityUOM")[0].text)
            self._check(
                picking_out, move_line.product_qty >= quantity_uom,
                _('Product {0} (id={1}): QuantityUOM is greater than that of the stock.move.'
                  ).format(product.name, product_id))
            partial['product_qty'] = quantity_uom

            # <QuantityISO>
            quantity_iso = nspath(order_move,
                                  "warr:QuantityUOM")[0].attrib['QuantityISO']
            uom_iso_list = self.pool.get('product.uom').search(
                self.cr,
                self.uid, [('uom_iso', '=', quantity_iso)],
                context=self.context)
            if len(uom_iso_list) > 0 and move_line.product_uom and (
                    quantity_iso != move_line.product_uom.uom_iso):
                self._check(
                    picking_out, False,
                    _('Product {0} (id={1}): Attribute QuantityISO does not match the ISO code indicated of the original stock.move.'
                      ).format(product.name, product_id))
            else:
                if not move_line.product_uom:
                    product_uom = uom_iso_list[0]
                    partial['product_uom'] = product_uom
                else:
                    self._check(
                        picking_out,
                        move_line.product_uom.uom_iso == quantity_iso,
                        _('Product {0} (id={1}): Attribute QuantityISO does not match that of the stock.move.'
                          ).format(product.name, product_id))
                    partial['product_uom'] = move_line.product_uom.id

            # Checks <StorageLocation> and <StockType>
            # Notes: Check together with StorageLocation against location_id on stock.move - alarm if wrong.
            #        If free type (' ', '0', 'F') use the StorageLocation, otherwise location YBLK.
            storage_location = nspath(order_move,
                                      "warr:StorageLocation")[0].text
            stock_type = nspath(order_move, "warr:StockType")
            if move_line.location_id or move_line.location_dest_id:
                location_names = []
                if move_line.location_id:
                    location_names.append(move_line.location_id.name)
                if move_line.location_dest_id:
                    location_names.append(move_line.location_dest_id.name)

                if stock_type:
                    # If there exists the tag <StockType>, then we follow the rules.
                    stock_type = stock_type[0].text
                    if stock_type not in ('X', 'S', '2', '3', '0', 'F', ' '):
                        self._check(
                            picking_out, False,
                            _("Product {0} (id={1}): StockType had value '{2}', which is not allowed."
                              ).format(product.name, product_id, stock_type))
                    elif stock_type in ('0', 'F', ' '):
                        self._check(
                            picking_out, storage_location in location_names,
                            _('Product {0} (id={1}): StorageLocation {2} and StockType {3} does not match with the location indicated in the stock.move {4}'
                              ).format(product.name, product_id,
                                       storage_location, stock_type,
                                       location_names))
                    else:
                        self._check(
                            picking_out, 'YBLK' in location_names,
                            _("Product {0} (id={1}): StorageLocation must be 'YBLK' since StockType is not a free type."
                              ).format(product.name, product_id))
                else:
                    # If <StockType> does not exist, it just checks that the values match.
                    if storage_location not in location_names:
                        self._check(
                            picking_out, False,
                            _('Product {0} (id={1}): StorageLocation {2} does not match with the location indicated in the stock.move {3}'
                              ).format(product.name, product_id,
                                       storage_location, location_names))
            else:
                self._check(
                    picking_out, False,
                    _('Product {0} (id={1}): The stock move does not have a location_id.'
                      ).format(product.name, product_id))

            # <Serial Numbers>
            serial_numbers = nspath(order_move, "warr:SerialNumbers")
            if serial_numbers:
                serial_numbers = serial_numbers[0].text
                if move_line.serial_number_scanned:
                    self._check(
                        picking_out,
                        move_line.serial_number_scanned == serial_numbers,
                        _('Product {0} (id={1}): SerialNumbers does not match the serial_number_scanned indicated of the original stock.move.'
                          ).format(product.name, product_id))
                else:
                    stock_move_obj.write(
                        self.cr, self.uid, move_line.id,
                        {'serial_number_scanned': serial_numbers},
                        self.context)

        if self.success:
            picking_id = picking_out.id

            picking_out.message_post(
                _('Imported WAR file BookingVoucherID={0} BookingVoucherYear={1}'
                  ).format(booking_voucher_id, booking_voucher_year))
            if V8:
                for move in partials:
                    vals = partials[move]
                    new_move_id = stock_move_obj.split(
                        self.cr,
                        self.uid,
                        move,
                        vals['product_qty'],
                        restrict_lot_id=vals.get('restrict_lot_id', False),
                        context=self.context)
                    stock_move_obj.action_done(self.cr,
                                               self.uid, [new_move_id],
                                               context=self.context)
            else:
                backorder_id, picking_id = picking_out.wrapper_do_partial(
                    partials)

                # Pickings created by WARs as backorders are not sent to the warehouse, by default.
                if backorder_id:
                    stock_obj.write(self.cr,
                                    self.uid,
                                    backorder_id, {
                                        'do_not_send_to_warehouse': True,
                                    },
                                    context=self.context)

            picking_to_deliver = stock_obj.browse(self.cr,
                                                  self.uid,
                                                  picking_id,
                                                  context=self.context)
            picking_to_deliver.action_done()
            picking_to_deliver.set_stock_moves_done()

            # Stores the values associated to BookingVoucherId and BookingVoucherYear, for reference.
            move_ids = [move.id for move in picking_out.move_lines]
            stock_move_obj.write(
                self.cr, self.uid, move_ids, {
                    'yc_booking_voucher_id': booking_voucher_id,
                    'yc_booking_voucher_year': booking_voucher_year,
                }, self.context)
            self.mark_record(picking_out.id,
                             'stock.picking' if V8 else 'stock.picking.out')

            # The message is sent ONLY if we had success.
#             picking_out.message_post(body=_("""Your order has been shipped through {0} and it can be tracked in the next link:\
#                                             <br/>\
#                                             <a href='https://www.post.ch/swisspost-tracking?formattedParcelCodes={1}'>Track&Trace</a>\
#                                             """).format(picking_out.carrier_id.name, urllib.quote(picking_out.carrier_tracking_ref)),
#                                      type='comment',
#                                      subtype="mail.mt_comment",
#                                      context=self.context,
#                                      partner_ids=picking_out.carrier_id and [picking_out.carrier_id.partner_id.id] or [])
        else:
            raise Warning(
                'There where some errors in the WAR file: {0}'.format(
                    '\n'.join(self.errors)))

        return True