1# -*- encoding: utf-8 -*- 2# Part of Odoo. See LICENSE file for full copyright and licensing details. 3 4from odoo import api, fields, models, _ 5from odoo.exceptions import ValidationError 6 7import re 8 9 10class Efaktur(models.Model): 11 _name = "l10n_id_efaktur.efaktur.range" 12 _description = "Available E-faktur range" 13 14 company_id = fields.Many2one('res.company', required=True, default=lambda self: self.env.company) 15 max = fields.Char(compute='_compute_default', store=True, readonly=False) 16 min = fields.Char(compute='_compute_default', store=True, readonly=False) 17 available = fields.Integer(compute='_compute_available', store=True) 18 19 @api.model 20 def pop_number(self, company_id): 21 range = self.search([('company_id', '=', company_id)], order="min ASC", limit=1) 22 if not range: 23 return None 24 25 popped = int(range.min) 26 if int(range.min) >= int(range.max): 27 range.unlink() 28 else: 29 range.min = '%013d' % (popped + 1) 30 return popped 31 32 @api.model 33 def push_number(self, company_id, number): 34 return self.push_numbers(company_id, number, number) 35 36 @api.model 37 def push_numbers(self, company_id, min, max): 38 range_sup = self.search([('min', '=', '%013d' % (int(max) + 1))]) 39 if range_sup: 40 range_sup.min = '%013d' % int(min) 41 max = range_sup.max 42 43 range_low = self.search([('max', '=', '%013d' % (int(max) - 1))]) 44 if range_low: 45 range_sup.unlink() 46 range_low.max = '%013d' % int(max) 47 48 if not range_sup and not range_low: 49 self.create({ 50 'company_id': company_id, 51 'max': '%013d' % int(max), 52 'min': '%013d' % int(min), 53 }) 54 55 56 @api.constrains('min', 'max') 57 def _constrains_min_max(self): 58 for record in self: 59 if not len(record.min) == 13 or not len(record.max) == 13: 60 raise ValidationError(_("There should be 13 digits in each number.")) 61 62 if record.min[:-8] != record.max[:-8]: 63 raise ValidationError(_("First 5 digits should be same in Start Number and End Number.")) 64 65 if int(record.min[-8:]) > int(record.max[-8:]): 66 raise ValidationError(_("Last 8 digits of End Number should be greater than the last 8 digit of Start Number")) 67 68 if (int(record.max) - int(record.min)) > 10000: 69 raise ValidationError(_("The difference between the two numbers must not be greater than 10.000")) 70 71 # The number of records should always be very small, so it is ok to search in loop 72 if self.search([ 73 '&', ('id', '!=', record.id), '|', '|', 74 '&', ('min', '<=', record.max), ('max', '>=', record.max), 75 '&', ('min', '<=', record.min), ('max', '>=', record.min), 76 '&', ('min', '>=', record.min), ('max', '<=', record.max), 77 ]): 78 raise ValidationError(_('Efaktur interleaving range detected')) 79 80 @api.depends('min', 'max') 81 def _compute_available(self): 82 for record in self: 83 record.available = 1 + int(record.max) - int(record.min) 84 85 @api.depends('company_id') 86 def _compute_default(self): 87 for record in self: 88 query = """ 89 SELECT MAX(SUBSTRING(l10n_id_tax_number FROM 4)) 90 FROM account_move 91 WHERE l10n_id_tax_number IS NOT NULL 92 AND company_id = %s 93 """ 94 self.env.cr.execute(query, [record.company_id.id]) 95 max_used = int(self.env.cr.fetchone()[0] or 0) 96 max_available = int(self.env['l10n_id_efaktur.efaktur.range'].search([('company_id', '=', record.company_id.id)], order='max DESC', limit=1).max) 97 record.min = record.max = '%013d' % (max(max_available, max_used) + 1) 98 99 @api.onchange('min') 100 def _onchange_min(self): 101 self.min = '%013d' % int(re.sub(r'\D', '', self.min)) 102 if not self.max or int(self.min) > int(self.max): 103 self.max = self.min 104 105 @api.onchange('max') 106 def _onchange_max(self): 107 self.max = '%013d' % int(re.sub(r'\D', '', self.max)) 108 if not self.min or int(self.min) > int(self.max): 109 self.min = self.max 110