172 lines
6.4 KiB
Python
172 lines
6.4 KiB
Python
# -*- coding: utf-8 -*-
|
|
# Copyright 2021 Joan Marín <Github@JoanMarin>
|
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
|
|
|
import pytz
|
|
from dateutil import tz
|
|
from datetime import datetime
|
|
from odoo import api, fields, models, _
|
|
from odoo.exceptions import ValidationError
|
|
|
|
|
|
class IrSequence(models.Model):
|
|
_inherit = 'ir.sequence'
|
|
|
|
use_dian_control = fields.Boolean(string='Use DIAN Resolutions Control?')
|
|
remaining_numbers = fields.Integer(string='Remaining Numbers', default=False)
|
|
remaining_days = fields.Integer(string='Remaining Days', default=False)
|
|
dian_type = fields.Selection(
|
|
selection=[
|
|
('computer_generated_invoice', 'Computer Generated Invoice'),
|
|
('paper_invoice', 'Paper Invoice'),
|
|
('pos_invoice', 'POS Invoice')],
|
|
string='DIAN Type')
|
|
description = fields.Text(string='Resolution Description')
|
|
|
|
@api.model
|
|
def create(self, vals):
|
|
rec = super(IrSequence, self).create(vals)
|
|
|
|
for sequence_id in rec:
|
|
if sequence_id.use_dian_control:
|
|
sequence_id.check_active_resolution()
|
|
|
|
sequence_id.check_date_range_ids()
|
|
|
|
return rec
|
|
|
|
def write(self, vals):
|
|
res = super(IrSequence, self).write(vals)
|
|
|
|
for sequence_id in self:
|
|
if sequence_id.use_dian_control:
|
|
sequence_id.check_active_resolution()
|
|
|
|
sequence_id.check_date_range_ids()
|
|
|
|
return res
|
|
|
|
@api.onchange('use_dian_control')
|
|
def onchange_active_resolution(self):
|
|
for sequence_id in self:
|
|
sequence_id.use_date_range = True
|
|
|
|
def check_active_resolution(self):
|
|
msg1 = _('Final Date - Resolution, must be greater or equal than Final Date.')
|
|
|
|
if self.use_dian_control:
|
|
if self.implementation != 'no_gap':
|
|
self.implementation = 'no_gap'
|
|
|
|
if self.padding != 0:
|
|
self.padding = 0
|
|
|
|
if not self.use_date_range:
|
|
self.use_date_range = True
|
|
|
|
if self.suffix:
|
|
self.suffix = False
|
|
|
|
if self.number_increment != 1:
|
|
self.number_increment = 1
|
|
|
|
timezone = pytz.timezone(self.env.user.tz or 'America/Bogota')
|
|
from_zone = tz.gettz('UTC')
|
|
to_zone = tz.gettz(timezone.zone)
|
|
current_date = datetime.now().replace(tzinfo=from_zone)
|
|
current_date = current_date.astimezone(to_zone).strftime('%Y-%m-%d')
|
|
|
|
for date_range_id in self.date_range_ids:
|
|
number_next_actual = date_range_id.number_next_actual
|
|
date_from = datetime.strftime(date_range_id.date_from, '%Y-%m-%d')
|
|
date_to = datetime.strftime(date_range_id.date_to, '%Y-%m-%d')
|
|
date_to_resolution = datetime.strftime(
|
|
date_range_id.date_to_resolution, '%Y-%m-%d')
|
|
|
|
if date_to_resolution < date_to:
|
|
raise ValidationError(msg1)
|
|
|
|
if (number_next_actual >= date_range_id.number_from
|
|
and number_next_actual <= date_range_id.number_to
|
|
and current_date >= date_from
|
|
and current_date <= date_to):
|
|
if not date_range_id.active_resolution:
|
|
date_range_id.active_resolution = True
|
|
|
|
if date_range_id.prefix != self.prefix:
|
|
date_range_id.prefix = self.prefix
|
|
else:
|
|
date_range_id.active_resolution = False
|
|
|
|
if not date_range_id.prefix:
|
|
date_range_id.prefix = self.prefix
|
|
|
|
return True
|
|
|
|
def check_date_range_ids(self):
|
|
msg1 = _('Final Date must be greater or equal than Initial Date.')
|
|
msg2 = _('The Date Range must be unique or a date ' +
|
|
'must not be included in another Date Range.')
|
|
msg3 = _('Number To must be greater or equal than Number From.')
|
|
msg4 = _('The Number Next must be greater in one to Number To, to represent ' +
|
|
'a finished sequence or Number Next must be included in Number Range.')
|
|
msg5 = _('The system needs only one active DIAN resolution.')
|
|
msg6 = _('The system needs at least one active DIAN resolution.')
|
|
date_ranges = []
|
|
_active_resolution = 0
|
|
|
|
for date_range_id in self.date_range_ids:
|
|
if date_range_id.date_from and date_range_id.date_to:
|
|
if date_range_id.date_from > date_range_id.date_to:
|
|
raise ValidationError(msg1)
|
|
|
|
date_ranges.append((date_range_id.date_from, date_range_id.date_to))
|
|
date_ranges.sort(key=lambda date_range: date_range[0])
|
|
date_from = False
|
|
date_to = False
|
|
|
|
for date_range in date_ranges:
|
|
if not date_from and not date_to:
|
|
date_from = date_range[0]
|
|
date_to = date_range[1]
|
|
continue
|
|
|
|
if date_to < date_range[0]:
|
|
date_from = date_range[0]
|
|
date_to = date_range[1]
|
|
else:
|
|
raise ValidationError(msg2)
|
|
|
|
if date_range_id.number_from and date_range_id.number_to:
|
|
if date_range_id.number_from > date_range_id.number_to:
|
|
raise ValidationError(msg3)
|
|
elif (date_range_id.number_next_actual >
|
|
(date_range_id.number_to + 1)
|
|
or date_range_id.number_from >
|
|
date_range_id.number_next_actual):
|
|
raise ValidationError(msg4)
|
|
|
|
if date_range_id.active_resolution and self.use_dian_control:
|
|
_active_resolution += 1
|
|
|
|
if self.use_dian_control:
|
|
if _active_resolution > 1:
|
|
raise ValidationError(msg5)
|
|
|
|
if _active_resolution == 0:
|
|
raise ValidationError(msg6)
|
|
|
|
def _next(self, sequence_date=None):
|
|
msg = _('There is no active authorized invoicing resolution.')
|
|
date_range_ids = self.date_range_ids.filtered(lambda dr: dr.active_resolution)
|
|
|
|
if self.use_dian_control and not date_range_ids:
|
|
raise ValidationError(msg)
|
|
|
|
res = super(IrSequence, self)._next(sequence_date)
|
|
|
|
if self.use_dian_control:
|
|
self.check_active_resolution()
|
|
|
|
return res
|