示例#1
0
    def _compute_display_name(self):
        self2 = self.with_context(display_website=False)
        super(Partner, self2)._compute_display_name()

        # onchange uses the cache to retrieve value, we need to copy computed_value into the initial env
        for record, record2 in izip(self, self2):
            record.display_name = record2.display_name
示例#2
0
    def process_bank_statement_line(self, st_line_ids, data):
        """ Handles data sent from the bank statement reconciliation widget
            (and can otherwise serve as an old-API bridge)

            :param st_line_ids
            :param list of dicts data: must contains the keys
                'counterpart_aml_dicts', 'payment_aml_ids' and 'new_aml_dicts',
                whose value is the same as described in process_reconciliation
                except that ids are used instead of recordsets.
        """
        st_lines = self.env['account.bank.statement.line'].browse(st_line_ids)
        AccountMoveLine = self.env['account.move.line']
        ctx = dict(self._context, force_price_include=False)

        for st_line, datum in pycompat.izip(st_lines, data):
            payment_aml_rec = AccountMoveLine.browse(datum.get('payment_aml_ids', []))

            for aml_dict in datum.get('counterpart_aml_dicts', []):
                aml_dict['move_line'] = AccountMoveLine.browse(aml_dict['counterpart_aml_id'])
                del aml_dict['counterpart_aml_id']

            if datum.get('partner_id') is not None:
                st_line.write({'partner_id': datum['partner_id']})

            st_line.with_context(ctx).process_reconciliation(
                datum.get('counterpart_aml_dicts', []),
                payment_aml_rec,
                datum.get('new_aml_dicts', []))
示例#3
0
    def test_multiple(self):
        """ With two "concurrent" o2ms, exports the first line combined, then
        exports the rows for the first o2m, then the rows for the second o2m.
        """
        fields = ['const', 'child1/value', 'child2/value']
        child1 = [(0, False, {
            'value': v,
            'str': 'record%.02d' % index
        }) for index, v in pycompat.izip(itertools.count(), [4, 42, 36, 4, 13])
                  ]
        child2 = [(0, False, {
            'value': v,
            'str': 'record%.02d' % index
        }) for index, v in pycompat.izip(itertools.count(10),
                                         [8, 12, 8, 55, 33, 13])]

        self.assertEqual(
            self.export(child1=child1, child2=False, fields=fields), [
                [u'36', u'4', False],
                ['', u'42', ''],
                ['', u'36', ''],
                ['', u'4', ''],
                ['', u'13', ''],
            ])
        self.assertEqual(
            self.export(child1=False, child2=child2, fields=fields), [
                [u'36', False, u'8'],
                ['', '', u'12'],
                ['', '', u'8'],
                ['', '', u'55'],
                ['', '', u'33'],
                ['', '', u'13'],
            ])
        self.assertEqual(
            self.export(child1=child1, child2=child2, fields=fields), [
                [u'36', u'4', u'8'],
                ['', u'42', ''],
                ['', u'36', ''],
                ['', u'4', ''],
                ['', u'13', ''],
                ['', '', u'12'],
                ['', '', u'8'],
                ['', '', u'55'],
                ['', '', u'33'],
                ['', '', u'13'],
            ])
示例#4
0
 def iter_tax_codes(self):
     keys = [c.value for c in self.sheet_tax_codes.row(0)]
     yield keys
     for i in range(1, self.sheet_tax_codes.nrows):
         row = (c.value for c in self.sheet_tax_codes.row(i))
         d = OrderedDict(pycompat.izip(keys, row))
         d['sign'] = int(d['sign'])
         d['sequence'] = int(d['sequence'])
         yield d
示例#5
0
def load_information_from_description_file(module, mod_path=None):
    """
    :param module: The name of the module (sale, purchase, ...)
    :param mod_path: Physical path of module, if not providedThe name of the module (sale, purchase, ...)
    """
    if not mod_path:
        mod_path = get_module_path(module, downloaded=True)
    manifest_file = module_manifest(mod_path)
    if manifest_file:
        # default values for descriptor
        info = {
            'application': False,
            'author': 'Eagle ERP',
            'auto_install': False,
            'category': 'Uncategorized',
            'depends': [],
            'description': '',
            'icon': get_module_icon(module),
            'installable': True,
            'license': 'LGPL-3',
            'post_load': None,
            'version': '1.0',
            'web': False,
            'sequence': 100,
            'summary': '',
            'website': '',
        }
        info.update(
            pycompat.izip(
                'depends data demo test init_xml update_xml demo_xml'.split(),
                iter(list, None)))

        f = tools.file_open(manifest_file, mode='rb')
        try:
            info.update(ast.literal_eval(pycompat.to_native(f.read())))
        finally:
            f.close()

        if not info.get('description'):
            readme_path = [
                opj(mod_path, x) for x in README
                if os.path.isfile(opj(mod_path, x))
            ]
            if readme_path:
                readme_text = tools.file_open(readme_path[0]).read()
                info['description'] = readme_text

        if 'active' in info:
            # 'active' has been renamed 'auto_install'
            info['auto_install'] = info['active']

        info['version'] = adapt_version(info['version'])
        return info

    _logger.debug('module %s: no manifest file found %s', module,
                  MANIFEST_NAMES)
    return {}
示例#6
0
        def chk(lst, verbose=False):
            pvs = []
            for v in lst:
                pv = parse_version(v)
                pvs.append(pv)
                if verbose:
                    print(v, pv)

            for a, b in pycompat.izip(pvs, pvs[1:]):
                assert a < b, '%s < %s == %s' % (a, b, a < b)
示例#7
0
    def _load_records_create(self, vals_list):
        partners = super(Partner,
                         self.with_context(_partners_skip_fields_sync=True)
                         )._load_records_create(vals_list)

        # batch up first part of _fields_sync
        # group partners by commercial_partner_id (if not self) and parent_id (if type == contact)
        groups = collections.defaultdict(list)
        for partner, vals in pycompat.izip(partners, vals_list):
            cp_id = None
            if vals.get(
                    'parent_id') and partner.commercial_partner_id != partner:
                cp_id = partner.commercial_partner_id.id

            add_id = None
            if partner.parent_id and partner.type == 'contact':
                add_id = partner.parent_id.id
            groups[(cp_id, add_id)].append(partner.id)

        for (cp_id, add_id), children in groups.items():
            # values from parents (commercial, regular) written to their common children
            to_write = {}
            # commercial fields from commercial partner
            if cp_id:
                to_write = self.browse(cp_id)._update_fields_values(
                    self._commercial_fields())
            # address fields from parent
            if add_id:
                parent = self.browse(add_id)
                for f in self._address_fields():
                    v = parent[f]
                    if v:
                        to_write[f] = v.id if isinstance(
                            v, models.BaseModel) else v
            if to_write:
                self.browse(children).write(to_write)

        # do the second half of _fields_sync the "normal" way
        for partner, vals in pycompat.izip(partners, vals_list):
            partner._children_sync(vals)
            partner._handle_first_contact_creation()
        return partners
示例#8
0
 def _are_archs_equal(self, arch1, arch2):
     # Note that comparing the strings would not be ok as attributes order
     # must not be relevant
     if arch1.tag != arch2.tag:
         return False
     if arch1.text != arch2.text:
         return False
     if arch1.tail != arch2.tail:
         return False
     if arch1.attrib != arch2.attrib:
         return False
     if len(arch1) != len(arch2):
         return False
     return all(
         self._are_archs_equal(arch1, arch2)
         for arch1, arch2 in pycompat.izip(arch1, arch2))
示例#9
0
 def get_products_price(self,
                        products,
                        quantities,
                        partners,
                        date=False,
                        uom_id=False):
     """ For a given pricelist, return price for products
     Returns: dict{product_id: product price}, in the given pricelist """
     self.ensure_one()
     return {
         product_id: res_tuple[0]
         for product_id, res_tuple in self._compute_price_rule(
             list(pycompat.izip(products, quantities, partners)),
             date=date,
             uom_id=uom_id).items()
     }
示例#10
0
 def create_view(self, archf, terms, **kwargs):
     view = self.env['ir.ui.view'].create({
         'name': 'test',
         'model': 'res.partner',
         'arch': archf % terms,
     })
     for lang, trans_terms in kwargs.items():
         for src, val in pycompat.izip(terms, trans_terms):
             self.env['ir.translation'].create({
                 'type': 'model_terms',
                 'name': 'ir.ui.view,arch_db',
                 'lang': lang,
                 'res_id': view.id,
                 'src': src,
                 'value': val,
                 'state': 'translated',
             })
     return view
示例#11
0
 def test_read_group_without_name_get(self):
     model = self.env['test_performance.base']
     expected = self.expected_read_group()
     # use read_group and check the expected result
     with self.assertQueryCount(__system__=1, demo=1):
         model.invalidate_cache()
         result = model.read_group([], ['partner_id', 'value'],
                                   ['partner_id'])
         self.assertEqual(len(result), len(expected))
         for res, exp in pycompat.izip(result, expected):
             self.assertEqual(res['__domain'], exp['__domain'])
             self.assertEqual(res['partner_id'][0], exp['partner_id'][0])
             self.assertEqual(res['partner_id_count'],
                              exp['partner_id_count'])
             self.assertEqual(res['value'], exp['value'])
     # now serialize to json, which should force evaluation
     with self.assertQueryCount(__system__=1, demo=1):
         json.dumps(result)
示例#12
0
 def create(self, vals_list):
     products = super(
         ProductProduct,
         self.with_context(create_product_product=True)).create(vals_list)
     for product, vals in pycompat.izip(products, vals_list):
         # When a unique variant is created from tmpl then the standard price is set by _set_standard_price
         if not (self.env.context.get('create_from_tmpl')
                 and len(product.product_tmpl_id.product_variant_ids) == 1):
             product._set_standard_price(vals.get('standard_price') or 0.0)
     # `_get_variant_id_for_combination` depends on existing variants
     self.clear_caches()
     self.env['product.template'].invalidate_cache(
         fnames=[
             'valid_archived_variant_ids', 'valid_existing_variant_ids',
             'product_variant_ids', 'product_variant_id',
             'product_variant_count'
         ],
         ids=products.mapped('product_tmpl_id').ids)
     return products
    def test_invoice_with_discount(self):
        """ Test invoice with a discount and check discount applied on both SO lines and an invoice lines """
        # Update discount and delivered quantity on SO lines
        self.sol_prod_order.write({'discount': 20.0})
        self.sol_serv_deliver.write({'discount': 20.0, 'qty_delivered': 4.0})
        self.sol_serv_order.write({'discount': -10.0})
        self.sol_prod_deliver.write({'qty_delivered': 2.0})

        for line in self.sale_order.order_line.filtered(lambda l: l.discount):
            product_price = line.price_unit * line.product_uom_qty
            self.assertEquals(line.discount, (product_price - line.price_subtotal) / product_price * 100, 'Discount should be applied on order line')

        # lines are in draft
        for line in self.sale_order.order_line:
            self.assertTrue(float_is_zero(line.untaxed_amount_to_invoice, precision_digits=2), "The amount to invoice should be zero, as the line is in draf state")
            self.assertTrue(float_is_zero(line.untaxed_amount_invoiced, precision_digits=2), "The invoiced amount should be zero, as the line is in draft state")

        self.sale_order.action_confirm()

        for line in self.sale_order.order_line:
            self.assertTrue(float_is_zero(line.untaxed_amount_invoiced, precision_digits=2), "The invoiced amount should be zero, as the line is in draft state")

        self.assertEquals(self.sol_serv_order.untaxed_amount_to_invoice, 297, "The untaxed amount to invoice is wrong")
        self.assertEquals(self.sol_serv_deliver.untaxed_amount_to_invoice, self.sol_serv_deliver.qty_delivered * self.sol_serv_deliver.price_reduce, "The untaxed amount to invoice should be qty deli * price reduce, so 4 * (180 - 36)")
        self.assertEquals(self.sol_prod_deliver.untaxed_amount_to_invoice, 140, "The untaxed amount to invoice should be qty deli * price reduce, so 4 * (180 - 36)")

        # Let's do an invoice with invoiceable lines
        payment = self.env['sale.advance.payment.inv'].with_context(self.context).create({
            'advance_payment_method': 'delivered'
        })
        payment.create_invoices()
        invoice = self.sale_order.invoice_ids[0]
        invoice.action_invoice_open()

        # Check discount appeared on both SO lines and invoice lines
        for line, inv_line in pycompat.izip(self.sale_order.order_line, invoice.invoice_line_ids):
            self.assertEquals(line.discount, inv_line.discount, 'Discount on lines of order and invoice should be same')
示例#14
0
    def create(self, vals_list):
        if self.env.context.get('import_file'):
            self._check_import_consistency(vals_list)
        for vals in vals_list:
            if vals.get('website'):
                vals['website'] = self._clean_website(vals['website'])
            if vals.get('parent_id'):
                vals['company_name'] = False
            # compute default image in create, because computing gravatar in the onchange
            # cannot be easily performed if default images are in the way
            if not vals.get('image'):
                vals['image'] = self._get_default_image(
                    vals.get('type'), vals.get('is_company'),
                    vals.get('parent_id'))
            tools.image_resize_images(vals, sizes={'image': (1024, None)})
        partners = super(Partner, self).create(vals_list)

        if self.env.context.get('_partners_skip_fields_sync'):
            return partners

        for partner, vals in pycompat.izip(partners, vals_list):
            partner._fields_sync(vals)
            partner._handle_first_contact_creation()
        return partners
示例#15
0
    def test_rounding_03(self):
        """ Test rounding methods with 3 digits. """
        def try_round(amount, expected, digits=3, method='HALF-UP'):
            value = float_round(amount,
                                precision_digits=digits,
                                rounding_method=method)
            result = float_repr(value, precision_digits=digits)
            self.assertEqual(
                result, expected,
                'Rounding error: got %s, expected %s' % (result, expected))

        try_round(2.6745, '2.675')
        try_round(-2.6745, '-2.675')
        try_round(2.6744, '2.674')
        try_round(-2.6744, '-2.674')
        try_round(0.0004, '0.000')
        try_round(-0.0004, '-0.000')
        try_round(357.4555, '357.456')
        try_round(-357.4555, '-357.456')
        try_round(457.4554, '457.455')
        try_round(-457.4554, '-457.455')

        # Try some rounding value with rounding method UP instead of HALF-UP
        # We use 8.175 because when normalizing 8.175 with precision_digits=3 it gives
        # us 8175,0000000001234 as value, and if not handle correctly the rounding UP
        # value will be incorrect (should be 8,175 and not 8,176)
        try_round(8.175, '8.175', method='UP')
        try_round(8.1751, '8.176', method='UP')
        try_round(-8.175, '-8.175', method='UP')
        try_round(-8.1751, '-8.176', method='UP')
        try_round(-6.000, '-6.000', method='UP')
        try_round(1.8, '2', 0, method='UP')
        try_round(-1.8, '-2', 0, method='UP')

        # Try some rounding value with rounding method DOWN instead of HALF-UP
        # We use 2.425 because when normalizing 2.425 with precision_digits=3 it gives
        # us 2424.9999999999995 as value, and if not handle correctly the rounding DOWN
        # value will be incorrect (should be 2.425 and not 2.424)
        try_round(2.425, '2.425', method='DOWN')
        try_round(2.4249, '2.424', method='DOWN')
        try_round(-2.425, '-2.425', method='DOWN')
        try_round(-2.4249, '-2.424', method='DOWN')
        try_round(-2.500, '-2.500', method='DOWN')
        try_round(1.8, '1', 0, method='DOWN')
        try_round(-1.8, '-1', 0, method='DOWN')

        # Extended float range test, inspired by Cloves Almeida's test on bug #882036.
        fractions = [.0, .015, .01499, .675, .67499, .4555, .4555, .45555]
        expecteds = ['.00', '.02', '.01', '.68', '.67', '.46', '.456', '.4556']
        precisions = [2, 2, 2, 2, 2, 2, 3, 4]
        # Note: max precision for double floats is 53 bits of precision or
        # 17 significant decimal digits
        for magnitude in range(7):
            for frac, exp, prec in pycompat.izip(fractions, expecteds,
                                                 precisions):
                for sign in [-1, 1]:
                    for x in range(0, 10000, 97):
                        n = x * 10**magnitude
                        f = sign * (n + frac)
                        f_exp = ('-' if f != 0 and sign == -1 else
                                 '') + str(n) + exp
                        try_round(f, f_exp, digits=prec)

        def try_zero(amount, expected):
            self.assertEqual(float_is_zero(amount,
                                           precision_digits=3), expected,
                             "Rounding error: %s should be zero!" % amount)

        try_zero(0.0002, True)
        try_zero(-0.0002, True)
        try_zero(0.00034, True)
        try_zero(0.0005, False)
        try_zero(-0.0005, False)
        try_zero(0.0008, False)
        try_zero(-0.0008, False)

        def try_compare(amount1, amount2, expected):
            self.assertEqual(
                float_compare(amount1, amount2, precision_digits=3), expected,
                "Rounding error, compare_amounts(%s,%s) should be %s" %
                (amount1, amount2, expected))

        try_compare(0.0003, 0.0004, 0)
        try_compare(-0.0003, -0.0004, 0)
        try_compare(0.0002, 0.0005, -1)
        try_compare(-0.0002, -0.0005, 1)
        try_compare(0.0009, 0.0004, 1)
        try_compare(-0.0009, -0.0004, -1)
        try_compare(557.4555, 557.4556, 0)
        try_compare(-557.4555, -557.4556, 0)
        try_compare(657.4444, 657.445, -1)
        try_compare(-657.4444, -657.445, 1)

        # Rounding to unusual rounding units (e.g. coin values)
        def try_round(amount,
                      expected,
                      precision_rounding=None,
                      method='HALF-UP'):
            value = float_round(amount,
                                precision_rounding=precision_rounding,
                                rounding_method=method)
            result = float_repr(value, precision_digits=2)
            self.assertEqual(
                result, expected,
                'Rounding error: got %s, expected %s' % (result, expected))

        try_round(-457.4554, '-457.45', precision_rounding=0.05)
        try_round(457.444, '457.50', precision_rounding=0.5)
        try_round(457.3, '455.00', precision_rounding=5)
        try_round(457.5, '460.00', precision_rounding=5)
        try_round(457.1, '456.00', precision_rounding=3)
        try_round(2.5, '2.50', precision_rounding=0.05, method='DOWN')
        try_round(-2.5, '-2.50', precision_rounding=0.05, method='DOWN')
示例#16
0
 def _compute_params(self):
     self_bin = self.with_context(bin_size=False,
                                  bin_size_params_store=False)
     for record, record_bin in pycompat.izip(self, self_bin):
         record.params = record_bin.params_store and safe_eval(
             record_bin.params_store, {'uid': self._uid})
示例#17
0
    def message_post(self, **kwargs):
        # OVERRIDE
        # /!\ 'default_res_id' in self._context is used to don't process attachment when using a form view.
        res = super(AccountInvoice, self).message_post(**kwargs)

        def _get_attachment_filename(attachment):
            # Handle both _Attachment namedtuple in mail.thread or ir.attachment.
            return hasattr(attachment, 'fname') and getattr(
                attachment, 'fname') or attachment.name

        def _get_attachment_content(attachment):
            # Handle both _Attachment namedtuple in mail.thread or ir.attachment.
            return hasattr(attachment, 'content') and getattr(
                attachment, 'content') or base64.b64decode(attachment.datas)

        if 'default_res_id' not in self._context and len(
                self) == 1 and self.state == 'draft' and self.type in (
                    'in_invoice', 'in_refund'):
            # Get attachments.
            # - 'attachments' is a namedtuple defined in mail.thread looking like:
            # _Attachment = namedtuple('Attachment', ('fname', 'content', 'info'))
            # - 'attachment_ids' is a list of ir.attachment records ids.
            attachments = kwargs.get('attachments', [])
            if kwargs.get('attachment_ids'):
                attachments += self.env['ir.attachment'].browse(
                    kwargs['attachment_ids'])

            for attachment in attachments:
                filename = _get_attachment_filename(attachment)
                content = _get_attachment_content(attachment)

                # Check if the attachment is a pdf.
                if not filename.endswith('.pdf'):
                    continue

                buffer = io.BytesIO(content)
                try:
                    reader = PdfFileReader(buffer)

                    # Search for Factur-x embedded file.
                    if reader.trailer['/Root'].get(
                            '/Names') and reader.trailer['/Root'][
                                '/Names'].get('/EmbeddedFiles'):
                        # N.B: embedded_files looks like:
                        # ['file.xml', {'/Type': '/Filespec', '/F': 'file.xml', '/EF': {'/F': IndirectObject(22, 0)}}]
                        embedded_files = reader.trailer['/Root']['/Names'][
                            '/EmbeddedFiles']['/Names']
                        # '[::2]' because it's a list [fn_1, content_1, fn_2, content_2, ..., fn_n, content_2]
                        for filename_obj, content_obj in list(
                                pycompat.izip(embedded_files,
                                              embedded_files[1:]))[::2]:
                            content = content_obj.getObject(
                            )['/EF']['/F'].getData()

                            if filename_obj == 'factur-x.xml':
                                try:
                                    tree = etree.fromstring(content)
                                except:
                                    continue

                                self._import_facturx_invoice(tree)
                                buffer.close()
                                return res
                except:
                    # Malformed PDF.
                    pass
                buffer.close()
        return res
示例#18
0
 def iter_fiscal_pos_map(self):
     keys = [c.value for c in self.sheet_fiscal_pos_map.row(0)]
     yield keys
     for i in range(1, self.sheet_fiscal_pos_map.nrows):
         row = (c.value for c in self.sheet_fiscal_pos_map.row(i))
         yield OrderedDict(pycompat.izip(keys, row))
示例#19
0
 def update(self, records, field, values):
     """ Set the values of ``field`` for several ``records``. """
     key = records.env.cache_key(field)
     self._data[key][field].update(pycompat.izip(records._ids, values))
示例#20
0
    errors = 0

    def try_round(amount, expected, precision_digits=3):
        global count, errors; count += 1
        result = float_repr(float_round(amount, precision_digits=precision_digits),
                            precision_digits=precision_digits)
        if result != expected:
            errors += 1
            print('###!!! Rounding error: got %s , expected %s' % (result, expected))

    # Extended float range test, inspired by Cloves Almeida's test on bug #882036.
    fractions = [.0, .015, .01499, .675, .67499, .4555, .4555, .45555]
    expecteds = ['.00', '.02', '.01', '.68', '.67', '.46', '.456', '.4556']
    precisions = [2, 2, 2, 2, 2, 2, 3, 4]
    for magnitude in range(7):
        for frac, exp, prec in pycompat.izip(fractions, expecteds, precisions):
            for sign in [-1,1]:
                for x in range(0, 10000, 97):
                    n = x * 10**magnitude
                    f = sign * (n + frac)
                    f_exp = ('-' if f != 0 and sign == -1 else '') + str(n) + exp
                    try_round(f, f_exp, precision_digits=prec)

    stop = time.time()

    # Micro-bench results:
    # 47130 round calls in 0.422306060791 secs, with Python 2.6.7 on Core i3 x64
    # with decimal:
    # 47130 round calls in 6.612248100021 secs, with Python 2.6.7 on Core i3 x64
    print(count, " round calls, ", errors, "errors, done in ", (stop-start), 'secs')
示例#21
0
 def iter_taxes(self):
     keys = [c.value for c in self.sheet_taxes.row(0)]
     yield keys
     for i in range(1, self.sheet_taxes.nrows):
         row = (c.value for c in self.sheet_taxes.row(i))
         yield OrderedDict(pycompat.izip(keys, row))
示例#22
0
 def _price_get_multi(self, pricelist, products_by_qty_by_partner):
     """ Mono pricelist, multi product - return price per product """
     return pricelist.get_products_price(
         list(pycompat.izip(**products_by_qty_by_partner)))
示例#23
0
    def fast_counterpart_creation(self):
        """This function is called when confirming a bank statement and will allow to automatically process lines without
        going in the bank reconciliation widget. By setting an account_id on bank statement lines, it will create a journal
        entry using that account to counterpart the bank account
        """
        payment_list = []
        move_list = []
        account_type_receivable = self.env.ref(
            'account.data_account_type_receivable')
        already_done_stmt_line_ids = [
            a['statement_line_id'][0]
            for a in self.env['account.move.line'].read_group([(
                'statement_line_id', 'in',
                self.ids)], ['statement_line_id'], ['statement_line_id'])
        ]
        managed_st_line = []
        for st_line in self:
            # Technical functionality to automatically reconcile by creating a new move line
            if st_line.account_id and not st_line.id in already_done_stmt_line_ids:
                managed_st_line.append(st_line.id)
                # Create payment vals
                total = st_line.amount
                payment_methods = (
                    total > 0
                ) and st_line.journal_id.inbound_payment_method_ids or st_line.journal_id.outbound_payment_method_ids
                currency = st_line.journal_id.currency_id or st_line.company_id.currency_id
                partner_type = 'customer' if st_line.account_id.user_type_id == account_type_receivable else 'supplier'
                payment_list.append({
                    'payment_method_id':
                    payment_methods and payment_methods[0].id or False,
                    'payment_type':
                    total > 0 and 'inbound' or 'outbound',
                    'partner_id':
                    st_line.partner_id.id,
                    'partner_type':
                    partner_type,
                    'journal_id':
                    st_line.statement_id.journal_id.id,
                    'payment_date':
                    st_line.date,
                    'state':
                    'reconciled',
                    'currency_id':
                    currency.id,
                    'amount':
                    abs(total),
                    'communication':
                    st_line._get_communication(
                        payment_methods[0] if payment_methods else False),
                    'name':
                    st_line.statement_id.name
                    or _("Bank Statement %s") % st_line.date,
                })

                # Create move and move line vals
                move_vals = st_line._prepare_reconciliation_move(
                    st_line.statement_id.name)
                aml_dict = {
                    'name': st_line.name,
                    'debit': st_line.amount < 0 and -st_line.amount or 0.0,
                    'credit': st_line.amount > 0 and st_line.amount or 0.0,
                    'account_id': st_line.account_id.id,
                    'partner_id': st_line.partner_id.id,
                    'statement_line_id': st_line.id,
                }
                st_line._prepare_move_line_for_currency(
                    aml_dict, st_line.date or fields.Date.context_today())
                move_vals['line_ids'] = [(0, 0, aml_dict)]
                balance_line = self._prepare_reconciliation_move_line(
                    move_vals, -aml_dict['debit']
                    if st_line.amount < 0 else aml_dict['credit'])
                move_vals['line_ids'].append((0, 0, balance_line))
                move_list.append(move_vals)

        # Creates
        payment_ids = self.env['account.payment'].create(payment_list)
        for payment_id, move_vals in pycompat.izip(payment_ids, move_list):
            for line in move_vals['line_ids']:
                line[2]['payment_id'] = payment_id.id
        move_ids = self.env['account.move'].create(move_list)
        move_ids.post()

        for move, st_line, payment in pycompat.izip(
                move_ids, self.browse(managed_st_line), payment_ids):
            st_line.write({'move_name': move.name})
            payment.write({'payment_reference': move.name})
示例#24
0
    def test_create_multi(self):
        """ create for multiple records """
        # assumption: 'res.bank' does not override 'create'
        vals_list = [{'name': name} for name in ('Foo', 'Bar', 'Baz')]
        vals_list[0]['email'] = '*****@*****.**'
        for vals in vals_list:
            record = self.env['res.bank'].create(vals)
            self.assertEqual(len(record), 1)
            self.assertEqual(record.name, vals['name'])
            self.assertEqual(record.email, vals.get('email', False))

        records = self.env['res.bank'].create([])
        self.assertFalse(records)

        records = self.env['res.bank'].create(vals_list)
        self.assertEqual(len(records), len(vals_list))
        for record, vals in pycompat.izip(records, vals_list):
            self.assertEqual(record.name, vals['name'])
            self.assertEqual(record.email, vals.get('email', False))

        # create countries and states
        vals_list = [{
            'name':
            'Foo',
            'state_ids': [
                (0, 0, {
                    'name': 'North Foo',
                    'code': 'NF'
                }),
                (0, 0, {
                    'name': 'South Foo',
                    'code': 'SF'
                }),
                (0, 0, {
                    'name': 'West Foo',
                    'code': 'WF'
                }),
                (0, 0, {
                    'name': 'East Foo',
                    'code': 'EF'
                }),
            ],
        }, {
            'name':
            'Bar',
            'state_ids': [
                (0, 0, {
                    'name': 'North Bar',
                    'code': 'NB'
                }),
                (0, 0, {
                    'name': 'South Bar',
                    'code': 'SB'
                }),
            ],
        }]
        foo, bar = self.env['res.country'].create(vals_list)
        self.assertEqual(foo.name, 'Foo')
        self.assertCountEqual(foo.mapped('state_ids.code'),
                              ['NF', 'SF', 'WF', 'EF'])
        self.assertEqual(bar.name, 'Bar')
        self.assertCountEqual(bar.mapped('state_ids.code'), ['NB', 'SB'])
示例#25
0
    def test_order_to_payment_currency(self):
        """
            In order to test the Point of Sale in module, I will do a full flow from the sale to the payment and invoicing.
            I will use two products, one with price including a 10% tax, the other one with 5% tax excluded from the price.
            The order will be in a different currency than the company currency.
        """
        # Make sure the company is in USD
        self.env.cr.execute(
            "UPDATE res_company SET currency_id = %s WHERE id = %s",
            [self.env.ref('base.USD').id, self.env.user.company_id.id])

        # Demo data are crappy, clean-up the rates
        self.env['res.currency.rate'].search([]).unlink()
        self.env['res.currency.rate'].create({
            'name': '2010-01-01',
            'rate': 2.0,
            'currency_id': self.env.ref('base.EUR').id,
        })

        def compute_tax(product, price, qty=1, taxes=None):
            if not taxes:
                taxes = product.taxes_id.filtered(lambda t: t.company_id.id == self.env.user.id)
            currency = self.pos_config.pricelist_id.currency_id
            res = taxes.compute_all(price, currency, qty, product=product)
            untax = res['total_excluded']
            return untax, sum(tax.get('amount', 0.0) for tax in res['taxes'])

        # I click on create a new session button
        self.pos_config.open_session_cb()

        # I create a PoS order with 2 units of PCSC234 at 450 EUR (Tax Incl)
        # and 3 units of PCSC349 at 300 EUR. (Tax Excl)

        untax1, atax1 = compute_tax(self.product3, 450*0.95, 2)
        untax2, atax2 = compute_tax(self.product4, 300*0.95, 3)
        self.pos_order_pos0 = self.PosOrder.create({
            'company_id': self.company_id,
            'pricelist_id': self.partner1.property_product_pricelist.copy(default={'currency_id': self.env.ref('base.EUR').id}).id,
            'partner_id': self.partner1.id,
            'lines': [(0, 0, {
                'name': "OL/0001",
                'product_id': self.product3.id,
                'price_unit': 450,
                'discount': 0.0,
                'qty': 2.0,
                'tax_ids': [(6, 0, self.product3.taxes_id.ids)],
                'price_subtotal': untax1,
                'price_subtotal_incl': untax1 + atax1,
            }), (0, 0, {
                'name': "OL/0002",
                'product_id': self.product4.id,
                'price_unit': 300,
                'discount': 0.0,
                'qty': 3.0,
                'tax_ids': [(6, 0, self.product4.taxes_id.ids)],
                'price_subtotal': untax2,
                'price_subtotal_incl': untax2 + atax2,
            })],
            'amount_tax': atax1 + atax2,
            'amount_total': untax1 + untax2 + atax1 + atax2,
            'amount_paid': 0.0,
            'amount_return': 0.0,
        })

        # I check that the total of the order is now equal to (450*2 +
        # 300*3*1.05)*0.95
        self.assertLess(
            abs(self.pos_order_pos0.amount_total - (450 * 2 + 300 * 3 * 1.05) * 0.95),
            0.01, 'The order has a wrong total including tax and discounts')

        # I click on the "Make Payment" wizard to pay the PoS order with a
        # partial amount of 100.0 EUR
        context_make_payment = {"active_ids": [self.pos_order_pos0.id], "active_id": self.pos_order_pos0.id}
        self.pos_make_payment_0 = self.PosMakePayment.with_context(context_make_payment).create({
            'amount': 100.0
        })

        # I click on the validate button to register the payment.
        context_payment = {'active_id': self.pos_order_pos0.id}
        self.pos_make_payment_0.with_context(context_payment).check()

        # I check that the order is not marked as paid yet
        self.assertEqual(self.pos_order_pos0.state, 'draft', 'Order should be in draft state.')

        # On the second payment proposition, I check that it proposes me the
        # remaining balance which is 1790.0 EUR
        defs = self.pos_make_payment_0.with_context({'active_id': self.pos_order_pos0.id}).default_get(['amount'])

        self.assertLess(
            abs(defs['amount'] - ((450 * 2 + 300 * 3 * 1.05) * 0.95 - 100.0)), 0.01, "The remaining balance is incorrect.")

        #'I pay the remaining balance.
        context_make_payment = {
            "active_ids": [self.pos_order_pos0.id], "active_id": self.pos_order_pos0.id}

        self.pos_make_payment_1 = self.PosMakePayment.with_context(context_make_payment).create({
            'amount': (450 * 2 + 300 * 3 * 1.05) * 0.95 - 100.0
        })

        # I click on the validate button to register the payment.
        self.pos_make_payment_1.with_context(context_make_payment).check()

        # I check that the order is marked as paid
        self.assertEqual(self.pos_order_pos0.state, 'paid', 'Order should be in paid state.')

        # I generate the journal entries
        self.pos_order_pos0._create_account_move_line()

        # I test that the generated journal entry is attached to the PoS order
        self.assertTrue(self.pos_order_pos0.account_move, "Journal entry has not been attached to Pos order.")

        # Check the amounts
        debit_lines = self.pos_order_pos0.account_move.mapped('line_ids.debit')
        credit_lines = self.pos_order_pos0.account_move.mapped('line_ids.credit')
        amount_currency_lines = self.pos_order_pos0.account_move.mapped('line_ids.amount_currency')
        for a, b in pycompat.izip(sorted(debit_lines), [0.0, 0.0, 0.0, 0.0, 879.55]):
            self.assertAlmostEqual(a, b)
        for a, b in pycompat.izip(sorted(credit_lines), [0.0, 22.5, 40.91, 388.64, 427.5]):
            self.assertAlmostEqual(a, b)
        for a, b in pycompat.izip(sorted(amount_currency_lines), [-855.0, -777.27, -81.82, -45.0, 1752.75]):
            self.assertAlmostEqual(a, b)
示例#26
0
 def _compute_signup_valid(self):
     dt = now()
     for partner, partner_sudo in pycompat.izip(self, self.sudo()):
         partner.signup_valid = bool(partner_sudo.signup_token) and \
         (not partner_sudo.signup_expiration or dt <= partner_sudo.signup_expiration)