mirror of
https://github.com/OCA/intrastat-extrastat.git
synced 2025-02-16 17:13:41 +02:00
[MIG] intrastat_product: Migration to 13.0
This commit is contained in:
@@ -17,7 +17,7 @@
|
||||
|
||||
<record id="shipping_costs_exclude" model="product.product">
|
||||
<field name="name">Shipping costs</field>
|
||||
<field name="default_code">SHIP</field>
|
||||
<field name="default_code">SHIP_S</field>
|
||||
<field name="type">service</field>
|
||||
<field name="categ_id" ref="product.product_category_all"/>
|
||||
<field name="list_price">30</field>
|
||||
|
||||
@@ -6,37 +6,36 @@
|
||||
# @author Kumar Aberer <kumar.aberer@braintec-group.com>
|
||||
|
||||
{
|
||||
'name': 'Intrastat Product',
|
||||
'version': '12.0.1.0.0',
|
||||
'category': 'Intrastat',
|
||||
'license': 'AGPL-3',
|
||||
'summary': 'Base module for Intrastat Product',
|
||||
'author': 'brain-tec AG, Akretion, Noviat, '
|
||||
'Odoo Community Association (OCA)',
|
||||
'depends': [
|
||||
'intrastat_base',
|
||||
'product_harmonized_system',
|
||||
'sale_stock',
|
||||
'purchase_stock',
|
||||
'report_xlsx_helper',
|
||||
"name": "Intrastat Product",
|
||||
"version": "13.0.1.0.0",
|
||||
"category": "Intrastat",
|
||||
"license": "AGPL-3",
|
||||
"summary": "Base module for Intrastat Product",
|
||||
"author": "brain-tec AG, Akretion, Noviat, " "Odoo Community Association (OCA)",
|
||||
"depends": [
|
||||
"intrastat_base",
|
||||
"product_harmonized_system",
|
||||
"sale_stock",
|
||||
"purchase_stock",
|
||||
"report_xlsx_helper",
|
||||
],
|
||||
'excludes': ['account_intrastat'],
|
||||
'data': [
|
||||
'views/hs_code.xml',
|
||||
'views/intrastat_region.xml',
|
||||
'views/intrastat_unit.xml',
|
||||
'views/intrastat_transaction.xml',
|
||||
'views/intrastat_transport_mode.xml',
|
||||
'views/intrastat_product_declaration.xml',
|
||||
'views/res_config_settings.xml',
|
||||
'views/account_invoice.xml',
|
||||
'views/sale_order.xml',
|
||||
'views/stock_warehouse.xml',
|
||||
'security/intrastat_security.xml',
|
||||
'security/ir.model.access.csv',
|
||||
'data/intrastat_transport_mode.xml',
|
||||
'data/intrastat_unit.xml',
|
||||
"excludes": ["account_intrastat"],
|
||||
"data": [
|
||||
"views/hs_code.xml",
|
||||
"views/intrastat_region.xml",
|
||||
"views/intrastat_unit.xml",
|
||||
"views/intrastat_transaction.xml",
|
||||
"views/intrastat_transport_mode.xml",
|
||||
"views/intrastat_product_declaration.xml",
|
||||
"views/res_config_settings.xml",
|
||||
"views/account_move.xml",
|
||||
"views/sale_order.xml",
|
||||
"views/stock_warehouse.xml",
|
||||
"security/intrastat_security.xml",
|
||||
"security/ir.model.access.csv",
|
||||
"data/intrastat_transport_mode.xml",
|
||||
"data/intrastat_unit.xml",
|
||||
],
|
||||
'demo': ['demo/intrastat_demo.xml'],
|
||||
'installable': True,
|
||||
"demo": ["demo/intrastat_demo.xml"],
|
||||
"installable": True,
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from . import res_company
|
||||
from . import res_config_settings
|
||||
from . import account_invoice
|
||||
from . import account_move
|
||||
from . import hs_code
|
||||
from . import intrastat_product_declaration
|
||||
from . import intrastat_region
|
||||
|
||||
@@ -6,44 +6,51 @@
|
||||
from odoo import api, fields, models
|
||||
|
||||
|
||||
class AccountInvoice(models.Model):
|
||||
_inherit = 'account.invoice'
|
||||
class AccountMove(models.Model):
|
||||
_inherit = "account.move"
|
||||
|
||||
intrastat_transaction_id = fields.Many2one(
|
||||
comodel_name='intrastat.transaction',
|
||||
string='Intrastat Transaction Type',
|
||||
ondelete='restrict', track_visibility='onchange',
|
||||
help="Intrastat nature of transaction")
|
||||
comodel_name="intrastat.transaction",
|
||||
string="Intrastat Transaction Type",
|
||||
ondelete="restrict",
|
||||
track_visibility="onchange",
|
||||
help="Intrastat nature of transaction",
|
||||
)
|
||||
intrastat_transport_id = fields.Many2one(
|
||||
comodel_name='intrastat.transport_mode',
|
||||
string='Intrastat Transport Mode',
|
||||
ondelete='restrict')
|
||||
comodel_name="intrastat.transport_mode",
|
||||
string="Intrastat Transport Mode",
|
||||
ondelete="restrict",
|
||||
)
|
||||
src_dest_country_id = fields.Many2one(
|
||||
comodel_name='res.country',
|
||||
string='Origin/Destination Country',
|
||||
compute='_compute_intrastat_country',
|
||||
store=True, compute_sudo=True,
|
||||
help="Destination country for dispatches. Origin country for "
|
||||
"arrivals.")
|
||||
comodel_name="res.country",
|
||||
string="Origin/Destination Country",
|
||||
compute="_compute_intrastat_country",
|
||||
store=True,
|
||||
compute_sudo=True,
|
||||
help="Destination country for dispatches. Origin country for " "arrivals.",
|
||||
)
|
||||
intrastat_country = fields.Boolean(
|
||||
compute='_compute_intrastat_country', string='Intrastat Country',
|
||||
store=True, compute_sudo=True)
|
||||
compute="_compute_intrastat_country",
|
||||
string="Intrastat Country",
|
||||
store=True,
|
||||
compute_sudo=True,
|
||||
)
|
||||
src_dest_region_id = fields.Many2one(
|
||||
comodel_name='intrastat.region',
|
||||
string='Origin/Destination Region',
|
||||
comodel_name="intrastat.region",
|
||||
string="Origin/Destination Region",
|
||||
default=lambda self: self._default_src_dest_region_id(),
|
||||
help="Origin region for dispatches, destination region for "
|
||||
"arrivals. This field is used for the Intrastat Declaration.",
|
||||
ondelete='restrict')
|
||||
ondelete="restrict",
|
||||
)
|
||||
intrastat = fields.Char(
|
||||
string='Intrastat Declaration',
|
||||
related='company_id.intrastat')
|
||||
string="Intrastat Declaration", related="company_id.intrastat"
|
||||
)
|
||||
|
||||
@api.depends('partner_shipping_id.country_id', 'partner_id.country_id')
|
||||
@api.depends("partner_shipping_id.country_id", "partner_id.country_id")
|
||||
def _compute_intrastat_country(self):
|
||||
for inv in self:
|
||||
country = inv.partner_shipping_id.country_id\
|
||||
or inv.partner_id.country_id
|
||||
country = inv.partner_shipping_id.country_id or inv.partner_id.country_id
|
||||
if not country:
|
||||
country = inv.company_id.country_id
|
||||
inv.src_dest_country_id = country.id
|
||||
@@ -51,19 +58,19 @@ class AccountInvoice(models.Model):
|
||||
|
||||
@api.model
|
||||
def _default_src_dest_region_id(self):
|
||||
rco = self.env['res.company']
|
||||
rco = self.env["res.company"]
|
||||
company = rco._company_default_get()
|
||||
return company.intrastat_region_id
|
||||
|
||||
|
||||
class AccountInvoiceLine(models.Model):
|
||||
_inherit = 'account.invoice.line'
|
||||
class AccountMoveLine(models.Model):
|
||||
_inherit = "account.move.line"
|
||||
|
||||
hs_code_id = fields.Many2one(
|
||||
comodel_name='hs.code',
|
||||
string='Intrastat Code', ondelete='restrict')
|
||||
comodel_name="hs.code", string="Intrastat Code", ondelete="restrict"
|
||||
)
|
||||
|
||||
@api.onchange('product_id')
|
||||
@api.onchange("product_id")
|
||||
def intrastat_product_id_change(self):
|
||||
if self.product_id:
|
||||
hs_code = self.product_id.get_hs_code_recursively()
|
||||
@@ -3,7 +3,7 @@
|
||||
# @author Alexis de Lattre <alexis.delattre@akretion.com>
|
||||
# @author Luc de Meyer <info@noviat.com>
|
||||
|
||||
from odoo import api, fields, models, _
|
||||
from odoo import _, api, fields, models
|
||||
from odoo.exceptions import ValidationError
|
||||
|
||||
|
||||
@@ -11,20 +11,26 @@ class HSCode(models.Model):
|
||||
_inherit = "hs.code"
|
||||
|
||||
intrastat_unit_id = fields.Many2one(
|
||||
comodel_name='intrastat.unit',
|
||||
string='Intrastat Supplementary Unit')
|
||||
comodel_name="intrastat.unit", string="Intrastat Supplementary Unit"
|
||||
)
|
||||
|
||||
@api.constrains('local_code')
|
||||
@api.constrains("local_code")
|
||||
def _hs_code(self):
|
||||
if self.company_id.country_id.intrastat:
|
||||
if not self.local_code.isdigit():
|
||||
raise ValidationError(_(
|
||||
"Intrastat Codes should only contain digits. "
|
||||
"This is not the case for code '%s'.")
|
||||
% self.local_code)
|
||||
raise ValidationError(
|
||||
_(
|
||||
"Intrastat Codes should only contain digits. "
|
||||
"This is not the case for code '%s'."
|
||||
)
|
||||
% self.local_code
|
||||
)
|
||||
if len(self.local_code) != 8:
|
||||
raise ValidationError(_(
|
||||
"Intrastat Codes should "
|
||||
"contain 8 digits. This is not the case for "
|
||||
"Intrastat Code '%s' which has %d digits.")
|
||||
% (self.local_code, len(self.local_code)))
|
||||
raise ValidationError(
|
||||
_(
|
||||
"Intrastat Codes should "
|
||||
"contain 8 digits. This is not the case for "
|
||||
"Intrastat Code '%s' which has %d digits."
|
||||
)
|
||||
% (self.local_code, len(self.local_code))
|
||||
)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -5,19 +5,24 @@ from odoo import fields, models
|
||||
|
||||
|
||||
class IntrastatRegion(models.Model):
|
||||
_name = 'intrastat.region'
|
||||
_name = "intrastat.region"
|
||||
_description = "Intrastat Region"
|
||||
_sql_constraints = [
|
||||
('intrastat_region_code_unique',
|
||||
'UNIQUE(code, country_id)', # TODO add company_id ?
|
||||
'Code must be unique.')]
|
||||
(
|
||||
"intrastat_region_code_unique",
|
||||
"UNIQUE(code, country_id)", # TODO add company_id ?
|
||||
"Code must be unique.",
|
||||
)
|
||||
]
|
||||
|
||||
code = fields.Char(string='Code', required=True)
|
||||
code = fields.Char(string="Code", required=True)
|
||||
country_id = fields.Many2one(
|
||||
comodel_name='res.country',
|
||||
string='Country', required=True)
|
||||
name = fields.Char(string='Name', translate=True)
|
||||
description = fields.Char(string='Description')
|
||||
comodel_name="res.country", string="Country", required=True
|
||||
)
|
||||
name = fields.Char(string="Name", translate=True)
|
||||
description = fields.Char(string="Description")
|
||||
company_id = fields.Many2one(
|
||||
comodel_name='res.company', string='Company',
|
||||
default=lambda self: self.env['res.company']._company_default_get())
|
||||
comodel_name="res.company",
|
||||
string="Company",
|
||||
default=lambda self: self.env["res.company"]._company_default_get(),
|
||||
)
|
||||
|
||||
@@ -7,27 +7,32 @@ from odoo import api, fields, models
|
||||
|
||||
|
||||
class IntrastatTransaction(models.Model):
|
||||
_name = 'intrastat.transaction'
|
||||
_name = "intrastat.transaction"
|
||||
_description = "Intrastat Transaction"
|
||||
_order = 'code'
|
||||
_sql_constraints = [(
|
||||
'intrastat_transaction_code_unique',
|
||||
'UNIQUE(code, company_id)',
|
||||
'Code must be unique.')]
|
||||
_order = "code"
|
||||
_sql_constraints = [
|
||||
(
|
||||
"intrastat_transaction_code_unique",
|
||||
"UNIQUE(code, company_id)",
|
||||
"Code must be unique.",
|
||||
)
|
||||
]
|
||||
|
||||
code = fields.Char(string='Code', required=True)
|
||||
description = fields.Text(string='Description')
|
||||
code = fields.Char(string="Code", required=True)
|
||||
description = fields.Text(string="Description")
|
||||
company_id = fields.Many2one(
|
||||
comodel_name='res.company', string='Company',
|
||||
default=lambda self: self.env['res.company']._company_default_get())
|
||||
comodel_name="res.company",
|
||||
string="Company",
|
||||
default=lambda self: self.env["res.company"]._company_default_get(),
|
||||
)
|
||||
|
||||
@api.depends('code', 'description')
|
||||
@api.depends("code", "description")
|
||||
def name_get(self):
|
||||
res = []
|
||||
for this in self:
|
||||
name = this.code
|
||||
if this.description:
|
||||
name += ' ' + this.description
|
||||
name = len(name) > 55 and name[:55] + '...' or name
|
||||
name += " " + this.description
|
||||
name = len(name) > 55 and name[:55] + "..." or name
|
||||
res.append((this.id, name))
|
||||
return res
|
||||
|
||||
@@ -7,22 +7,21 @@ from odoo import api, fields, models
|
||||
|
||||
|
||||
class IntrastatTransportMode(models.Model):
|
||||
_name = 'intrastat.transport_mode'
|
||||
_name = "intrastat.transport_mode"
|
||||
_description = "Intrastat Transport Mode"
|
||||
_order = 'code'
|
||||
_sql_constraints = [(
|
||||
'intrastat_transport_code_unique',
|
||||
'UNIQUE(code)',
|
||||
'Code must be unique.')]
|
||||
_order = "code"
|
||||
_sql_constraints = [
|
||||
("intrastat_transport_code_unique", "UNIQUE(code)", "Code must be unique.")
|
||||
]
|
||||
|
||||
code = fields.Char(string='Code', required=True)
|
||||
name = fields.Char(string='Name', required=True, translate=True)
|
||||
description = fields.Char(string='Description', translate=True)
|
||||
code = fields.Char(string="Code", required=True)
|
||||
name = fields.Char(string="Name", required=True, translate=True)
|
||||
description = fields.Char(string="Description", translate=True)
|
||||
|
||||
@api.depends('name', 'code')
|
||||
@api.depends("name", "code")
|
||||
def name_get(self):
|
||||
res = []
|
||||
for mode in self:
|
||||
name = '%s. %s' % (mode.code, mode.name)
|
||||
name = "{}. {}".format(mode.code, mode.name)
|
||||
res.append((mode.id, name))
|
||||
return res
|
||||
|
||||
@@ -7,13 +7,15 @@ from odoo import fields, models
|
||||
|
||||
|
||||
class IntrastatUnit(models.Model):
|
||||
_name = 'intrastat.unit'
|
||||
_description = 'Intrastat Supplementary Units'
|
||||
_name = "intrastat.unit"
|
||||
_description = "Intrastat Supplementary Units"
|
||||
|
||||
name = fields.Char(string='Name', required=True)
|
||||
description = fields.Char(string='Description', required=True)
|
||||
name = fields.Char(string="Name", required=True)
|
||||
description = fields.Char(string="Description", required=True)
|
||||
uom_id = fields.Many2one(
|
||||
comodel_name='uom.uom', string='Regular UoM',
|
||||
comodel_name="uom.uom",
|
||||
string="Regular UoM",
|
||||
help="Select the regular Unit of Measure of Odoo that corresponds "
|
||||
"to this Intrastat Supplementary Unit.")
|
||||
"to this Intrastat Supplementary Unit.",
|
||||
)
|
||||
active = fields.Boolean(default=True)
|
||||
|
||||
@@ -7,66 +7,89 @@ from odoo import api, fields, models
|
||||
|
||||
|
||||
class ResCompany(models.Model):
|
||||
_inherit = 'res.company'
|
||||
_inherit = "res.company"
|
||||
|
||||
intrastat_incoterm_id = fields.Many2one(
|
||||
comodel_name='account.incoterms',
|
||||
string='Default Incoterm for Intrastat',
|
||||
comodel_name="account.incoterms",
|
||||
string="Default Incoterm for Intrastat",
|
||||
help="International Commercial Terms are a series of "
|
||||
"predefined commercial terms used in international "
|
||||
"transactions.")
|
||||
"predefined commercial terms used in international "
|
||||
"transactions.",
|
||||
)
|
||||
intrastat_arrivals = fields.Selection(
|
||||
selection='_intrastat_arrivals', string='Arrivals',
|
||||
default='extended', required=True)
|
||||
selection="_intrastat_arrivals",
|
||||
string="Arrivals",
|
||||
default="extended",
|
||||
required=True,
|
||||
)
|
||||
intrastat_dispatches = fields.Selection(
|
||||
selection='_intrastat_dispatches', string='Dispatches',
|
||||
default='extended', required=True)
|
||||
selection="_intrastat_dispatches",
|
||||
string="Dispatches",
|
||||
default="extended",
|
||||
required=True,
|
||||
)
|
||||
intrastat_transport_id = fields.Many2one(
|
||||
comodel_name='intrastat.transport_mode',
|
||||
string='Default Transport Mode', ondelete='restrict')
|
||||
comodel_name="intrastat.transport_mode",
|
||||
string="Default Transport Mode",
|
||||
ondelete="restrict",
|
||||
)
|
||||
intrastat = fields.Char(
|
||||
string='Intrastat Declaration', store=True, readonly=True,
|
||||
compute='_compute_intrastat')
|
||||
string="Intrastat Declaration",
|
||||
store=True,
|
||||
readonly=True,
|
||||
compute="_compute_intrastat",
|
||||
)
|
||||
intrastat_region_id = fields.Many2one(
|
||||
comodel_name='intrastat.region',
|
||||
string='Default Intrastat Region')
|
||||
comodel_name="intrastat.region", string="Default Intrastat Region"
|
||||
)
|
||||
intrastat_transaction_out_invoice = fields.Many2one(
|
||||
comodel_name='intrastat.transaction',
|
||||
string='Default Intrastat Transaction For Customer Invoice')
|
||||
comodel_name="intrastat.transaction",
|
||||
string="Default Intrastat Transaction For Customer Invoice",
|
||||
)
|
||||
intrastat_transaction_out_refund = fields.Many2one(
|
||||
comodel_name='intrastat.transaction',
|
||||
string='Default Intrastat Transaction for Customer Refunds')
|
||||
comodel_name="intrastat.transaction",
|
||||
string="Default Intrastat Transaction for Customer Refunds",
|
||||
)
|
||||
intrastat_transaction_in_invoice = fields.Many2one(
|
||||
comodel_name='intrastat.transaction',
|
||||
string='Default Intrastat Transaction For Supplier Invoices')
|
||||
comodel_name="intrastat.transaction",
|
||||
string="Default Intrastat Transaction For Supplier Invoices",
|
||||
)
|
||||
intrastat_transaction_in_refund = fields.Many2one(
|
||||
comodel_name='intrastat.transaction',
|
||||
string='Default Intrastat Transaction For Supplier Refunds')
|
||||
comodel_name="intrastat.transaction",
|
||||
string="Default Intrastat Transaction For Supplier Refunds",
|
||||
)
|
||||
intrastat_accessory_costs = fields.Boolean(
|
||||
string='Include Accessory Costs in Fiscal Value of Product')
|
||||
string="Include Accessory Costs in Fiscal Value of Product"
|
||||
)
|
||||
|
||||
@api.model
|
||||
def _intrastat_arrivals(self):
|
||||
return [
|
||||
('exempt', 'Exempt'),
|
||||
('standard', 'Standard'),
|
||||
('extended', 'Extended')]
|
||||
("exempt", "Exempt"),
|
||||
("standard", "Standard"),
|
||||
("extended", "Extended"),
|
||||
]
|
||||
|
||||
@api.model
|
||||
def _intrastat_dispatches(self):
|
||||
return [
|
||||
('exempt', 'Exempt'),
|
||||
('standard', 'Standard'),
|
||||
('extended', 'Extended')]
|
||||
("exempt", "Exempt"),
|
||||
("standard", "Standard"),
|
||||
("extended", "Extended"),
|
||||
]
|
||||
|
||||
@api.depends('intrastat_arrivals', 'intrastat_dispatches')
|
||||
@api.depends("intrastat_arrivals", "intrastat_dispatches")
|
||||
def _compute_intrastat(self):
|
||||
for this in self:
|
||||
if this.intrastat_arrivals == 'exempt' \
|
||||
and this.intrastat_dispatches == 'exempt':
|
||||
this.intrastat = 'exempt'
|
||||
elif this.intrastat_arrivals == 'extended' \
|
||||
or this.intrastat_dispatches == 'extended':
|
||||
this.intrastat = 'extended'
|
||||
if (
|
||||
this.intrastat_arrivals == "exempt"
|
||||
and this.intrastat_dispatches == "exempt"
|
||||
):
|
||||
this.intrastat = "exempt"
|
||||
elif (
|
||||
this.intrastat_arrivals == "extended"
|
||||
or this.intrastat_dispatches == "extended"
|
||||
):
|
||||
this.intrastat = "extended"
|
||||
else:
|
||||
this.intrastat = 'standard'
|
||||
this.intrastat = "standard"
|
||||
|
||||
@@ -6,28 +6,38 @@ from odoo import fields, models
|
||||
|
||||
|
||||
class ResConfigSettings(models.TransientModel):
|
||||
_inherit = 'res.config.settings'
|
||||
_inherit = "res.config.settings"
|
||||
|
||||
intrastat_incoterm_id = fields.Many2one(
|
||||
related='company_id.intrastat_incoterm_id', readonly=False)
|
||||
related="company_id.intrastat_incoterm_id", readonly=False
|
||||
)
|
||||
intrastat_arrivals = fields.Selection(
|
||||
related='company_id.intrastat_arrivals', readonly=False)
|
||||
related="company_id.intrastat_arrivals", readonly=False
|
||||
)
|
||||
intrastat_dispatches = fields.Selection(
|
||||
related='company_id.intrastat_dispatches', readonly=False)
|
||||
intrastat = fields.Char(related='company_id.intrastat')
|
||||
related="company_id.intrastat_dispatches", readonly=False
|
||||
)
|
||||
intrastat = fields.Char(related="company_id.intrastat")
|
||||
intrastat_transport_id = fields.Many2one(
|
||||
related='company_id.intrastat_transport_id', readonly=False)
|
||||
related="company_id.intrastat_transport_id", readonly=False
|
||||
)
|
||||
intrastat_region_id = fields.Many2one(
|
||||
related='company_id.intrastat_region_id', readonly=False)
|
||||
related="company_id.intrastat_region_id", readonly=False
|
||||
)
|
||||
intrastat_transaction_out_invoice = fields.Many2one(
|
||||
related='company_id.intrastat_transaction_out_invoice', readonly=False)
|
||||
related="company_id.intrastat_transaction_out_invoice", readonly=False
|
||||
)
|
||||
intrastat_transaction_out_refund = fields.Many2one(
|
||||
related='company_id.intrastat_transaction_out_refund', readonly=False)
|
||||
related="company_id.intrastat_transaction_out_refund", readonly=False
|
||||
)
|
||||
intrastat_transaction_in_invoice = fields.Many2one(
|
||||
related='company_id.intrastat_transaction_in_invoice', readonly=False)
|
||||
related="company_id.intrastat_transaction_in_invoice", readonly=False
|
||||
)
|
||||
intrastat_transaction_in_refund = fields.Many2one(
|
||||
related='company_id.intrastat_transaction_in_refund', readonly=False)
|
||||
related="company_id.intrastat_transaction_in_refund", readonly=False
|
||||
)
|
||||
intrastat_accessory_costs = fields.Boolean(
|
||||
related='company_id.intrastat_accessory_costs', readonly=False)
|
||||
country_id = fields.Many2one(related='company_id.country_id')
|
||||
country_code = fields.Char(related='company_id.country_id.code')
|
||||
related="company_id.intrastat_accessory_costs", readonly=False
|
||||
)
|
||||
country_id = fields.Many2one(related="company_id.country_id")
|
||||
country_code = fields.Char(related="company_id.country_id.code")
|
||||
|
||||
@@ -9,17 +9,19 @@ class SaleOrder(models.Model):
|
||||
_inherit = "sale.order"
|
||||
|
||||
intrastat_transport_id = fields.Many2one(
|
||||
comodel_name='intrastat.transport_mode', string='Transport Mode',
|
||||
help="This information is used in Intrastat reports")
|
||||
comodel_name="intrastat.transport_mode",
|
||||
string="Transport Mode",
|
||||
help="This information is used in Intrastat reports",
|
||||
)
|
||||
intrastat = fields.Selection(
|
||||
string='Intrastat Declaration',
|
||||
related='company_id.intrastat_dispatches')
|
||||
string="Intrastat Declaration", related="company_id.intrastat_dispatches"
|
||||
)
|
||||
|
||||
def _prepare_invoice(self):
|
||||
'''Copy destination country to invoice'''
|
||||
"""Copy destination country to invoice"""
|
||||
vals = super(SaleOrder, self)._prepare_invoice()
|
||||
if self.intrastat_transport_id:
|
||||
vals['intrastat_transport_id'] = self.intrastat_transport_id.id
|
||||
vals["intrastat_transport_id"] = self.intrastat_transport_id.id
|
||||
if self.warehouse_id.region_id:
|
||||
vals['src_dest_region_id'] = self.warehouse_id.region_id.id
|
||||
vals["src_dest_region_id"] = self.warehouse_id.region_id.id
|
||||
return vals
|
||||
|
||||
@@ -6,21 +6,22 @@ from odoo import fields, models
|
||||
|
||||
|
||||
class StockWarehouse(models.Model):
|
||||
_inherit = 'stock.warehouse'
|
||||
_inherit = "stock.warehouse"
|
||||
|
||||
region_id = fields.Many2one(
|
||||
comodel_name='intrastat.region', string='Intrastat Region')
|
||||
comodel_name="intrastat.region", string="Intrastat Region"
|
||||
)
|
||||
|
||||
|
||||
class StockLocation(models.Model):
|
||||
_inherit = 'stock.location'
|
||||
_inherit = "stock.location"
|
||||
|
||||
def get_intrastat_region(self):
|
||||
self.ensure_one()
|
||||
locations = self.search([('id', 'parent_of', self.id)])
|
||||
warehouses = self.env['stock.warehouse'].search([
|
||||
('lot_stock_id', 'in', locations.ids),
|
||||
('region_id', '!=', False)])
|
||||
locations = self.search([("id", "parent_of", self.id)])
|
||||
warehouses = self.env["stock.warehouse"].search(
|
||||
[("lot_stock_id", "in", locations.ids), ("region_id", "!=", False)]
|
||||
)
|
||||
if warehouses:
|
||||
return warehouses[0].region_id
|
||||
return None
|
||||
|
||||
@@ -4,192 +4,145 @@
|
||||
import logging
|
||||
|
||||
from odoo import models
|
||||
from odoo.tools.translate import translate, _
|
||||
from odoo.tools.translate import _, translate
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
IR_TRANSLATION_NAME = 'intrastat.product.report'
|
||||
IR_TRANSLATION_NAME = "intrastat.product.report"
|
||||
|
||||
|
||||
class IntrastatProductDeclarationXlsx(models.AbstractModel):
|
||||
_name = 'report.intrastat_product.product_declaration_xls'
|
||||
_inherit = 'report.report_xlsx.abstract'
|
||||
_name = "report.intrastat_product.product_declaration_xls"
|
||||
_inherit = "report.report_xlsx.abstract"
|
||||
|
||||
def _(self, src):
|
||||
lang = self.env.context.get('lang', 'en_US')
|
||||
val = translate(
|
||||
self.env.cr, IR_TRANSLATION_NAME, 'report', lang, src) or src
|
||||
lang = self.env.context.get("lang", "en_US")
|
||||
val = translate(self.env.cr, IR_TRANSLATION_NAME, "report", lang, src) or src
|
||||
return val
|
||||
|
||||
def _get_template(self, declaration):
|
||||
|
||||
template = {
|
||||
'product': {
|
||||
'header': {
|
||||
'type': 'string',
|
||||
'value': self._('Product'),
|
||||
"product": {
|
||||
"header": {"type": "string", "value": self._("Product")},
|
||||
"line": {
|
||||
"value": self._render("line.product_id and line.product_id.name")
|
||||
},
|
||||
'line': {
|
||||
'value': self._render(
|
||||
"line.product_id and line.product_id.name"),
|
||||
},
|
||||
'width': 36,
|
||||
"width": 36,
|
||||
},
|
||||
'product_origin_country': {
|
||||
'header': {
|
||||
'type': 'string',
|
||||
'value': self._('Product C/O'),
|
||||
"product_origin_country": {
|
||||
"header": {"type": "string", "value": self._("Product C/O")},
|
||||
"line": {
|
||||
"type": "string",
|
||||
"value": self._render("line.product_origin_country_id.name or ''"),
|
||||
},
|
||||
'line': {
|
||||
'type': 'string',
|
||||
'value': self._render(
|
||||
"line.product_origin_country_id.name or ''"),
|
||||
},
|
||||
'width': 28,
|
||||
"width": 28,
|
||||
},
|
||||
'hs_code': {
|
||||
'header': {
|
||||
'type': 'string',
|
||||
'value': self._('Intrastat Code'),
|
||||
"hs_code": {
|
||||
"header": {"type": "string", "value": self._("Intrastat Code")},
|
||||
"line": {
|
||||
"type": "string",
|
||||
"value": self._render("line.hs_code_id.local_code"),
|
||||
},
|
||||
'line': {
|
||||
'type': 'string',
|
||||
'value': self._render(
|
||||
"line.hs_code_id.local_code"),
|
||||
},
|
||||
'width': 14,
|
||||
"width": 14,
|
||||
},
|
||||
'src_dest_country': {
|
||||
'header': {
|
||||
'type': 'string',
|
||||
'value': self._('Country of Origin/Destination'),
|
||||
"src_dest_country": {
|
||||
"header": {
|
||||
"type": "string",
|
||||
"value": self._("Country of Origin/Destination"),
|
||||
},
|
||||
'line': {
|
||||
'type': 'string',
|
||||
'value': self._render(
|
||||
"line.src_dest_country_id.name"),
|
||||
"line": {
|
||||
"type": "string",
|
||||
"value": self._render("line.src_dest_country_id.name"),
|
||||
},
|
||||
'width': 28,
|
||||
"width": 28,
|
||||
},
|
||||
'amount_company_currency': {
|
||||
'header': {
|
||||
'type': 'string',
|
||||
'value': self._('Fiscal Value'),
|
||||
'format': self.format_theader_yellow_right,
|
||||
"amount_company_currency": {
|
||||
"header": {
|
||||
"type": "string",
|
||||
"value": self._("Fiscal Value"),
|
||||
"format": self.format_theader_yellow_right,
|
||||
},
|
||||
'line': {
|
||||
'type': 'number',
|
||||
'value': self._render("line.amount_company_currency"),
|
||||
'format': self.format_tcell_amount_right,
|
||||
"line": {
|
||||
"type": "number",
|
||||
"value": self._render("line.amount_company_currency"),
|
||||
"format": self.format_tcell_amount_right,
|
||||
},
|
||||
'width': 18,
|
||||
"width": 18,
|
||||
},
|
||||
'accessory_cost': {
|
||||
'header': {
|
||||
'type': 'string',
|
||||
'value': self._('Accessory Costs'),
|
||||
'format': self.format_theader_yellow_right,
|
||||
"accessory_cost": {
|
||||
"header": {
|
||||
"type": "string",
|
||||
"value": self._("Accessory Costs"),
|
||||
"format": self.format_theader_yellow_right,
|
||||
},
|
||||
'line': {
|
||||
'type': 'number',
|
||||
'value': self._render(
|
||||
"line.amount_accessory_cost_company_currency"),
|
||||
'format': self.format_tcell_amount_right,
|
||||
"line": {
|
||||
"type": "number",
|
||||
"value": self._render(
|
||||
"line.amount_accessory_cost_company_currency"
|
||||
),
|
||||
"format": self.format_tcell_amount_right,
|
||||
},
|
||||
'width': 18,
|
||||
"width": 18,
|
||||
},
|
||||
'transaction': {
|
||||
'header': {
|
||||
'type': 'string',
|
||||
'value': self._('Intrastat Transaction'),
|
||||
},
|
||||
'line': {
|
||||
'value': self._render(
|
||||
"line.transaction_id.display_name"),
|
||||
},
|
||||
'width': 36,
|
||||
"transaction": {
|
||||
"header": {"type": "string", "value": self._("Intrastat Transaction")},
|
||||
"line": {"value": self._render("line.transaction_id.display_name")},
|
||||
"width": 36,
|
||||
},
|
||||
'weight': {
|
||||
'header': {
|
||||
'type': 'string',
|
||||
'value': self._('Weight'),
|
||||
'format': self.format_theader_yellow_right,
|
||||
"weight": {
|
||||
"header": {
|
||||
"type": "string",
|
||||
"value": self._("Weight"),
|
||||
"format": self.format_theader_yellow_right,
|
||||
},
|
||||
'line': {
|
||||
'type': 'number',
|
||||
'value': self._render(
|
||||
"line.weight"),
|
||||
'format': self.format_tcell_amount_right,
|
||||
"line": {
|
||||
"type": "number",
|
||||
"value": self._render("line.weight"),
|
||||
"format": self.format_tcell_amount_right,
|
||||
},
|
||||
'width': 18,
|
||||
"width": 18,
|
||||
},
|
||||
'suppl_unit_qty': {
|
||||
'header': {
|
||||
'type': 'string',
|
||||
'value': self._('Suppl. Unit Qty'),
|
||||
'format': self.format_theader_yellow_right,
|
||||
"suppl_unit_qty": {
|
||||
"header": {
|
||||
"type": "string",
|
||||
"value": self._("Suppl. Unit Qty"),
|
||||
"format": self.format_theader_yellow_right,
|
||||
},
|
||||
'line': {
|
||||
"line": {
|
||||
# we don't specify a type here and rely on the
|
||||
# report_xlsx_helper type detection to use
|
||||
# write_string when suppl_unit_qty is zero
|
||||
'value': self._render(
|
||||
"line.suppl_unit_qty or ''"),
|
||||
'format': self.format_tcell_amount_right,
|
||||
"value": self._render("line.suppl_unit_qty or ''"),
|
||||
"format": self.format_tcell_amount_right,
|
||||
},
|
||||
'width': 18,
|
||||
"width": 18,
|
||||
},
|
||||
'suppl_unit': {
|
||||
'header': {
|
||||
'type': 'string',
|
||||
'value': self._('Suppl. Unit'),
|
||||
},
|
||||
'line': {
|
||||
'value': self._render(
|
||||
"line.intrastat_unit_id.name or ''"),
|
||||
},
|
||||
'width': 14,
|
||||
"suppl_unit": {
|
||||
"header": {"type": "string", "value": self._("Suppl. Unit")},
|
||||
"line": {"value": self._render("line.intrastat_unit_id.name or ''")},
|
||||
"width": 14,
|
||||
},
|
||||
'incoterm': {
|
||||
'header': {
|
||||
'type': 'string',
|
||||
'value': self._('Incoterm'),
|
||||
},
|
||||
'line': {
|
||||
'value': self._render("line.incoterm_id.name or ''"),
|
||||
},
|
||||
'width': 14,
|
||||
"incoterm": {
|
||||
"header": {"type": "string", "value": self._("Incoterm")},
|
||||
"line": {"value": self._render("line.incoterm_id.name or ''")},
|
||||
"width": 14,
|
||||
},
|
||||
'transport': {
|
||||
'header': {
|
||||
'type': 'string',
|
||||
'value': self._('Transport Mode'),
|
||||
},
|
||||
'line': {
|
||||
'value': self._render("line.transport_id.name or ''"),
|
||||
},
|
||||
'width': 14,
|
||||
"transport": {
|
||||
"header": {"type": "string", "value": self._("Transport Mode")},
|
||||
"line": {"value": self._render("line.transport_id.name or ''")},
|
||||
"width": 14,
|
||||
},
|
||||
'region': {
|
||||
'header': {
|
||||
'type': 'string',
|
||||
'value': self._('Intrastat Region'),
|
||||
},
|
||||
'line': {
|
||||
'value': self._render("line.region_id.name or ''"),
|
||||
},
|
||||
'width': 28,
|
||||
"region": {
|
||||
"header": {"type": "string", "value": self._("Intrastat Region")},
|
||||
"line": {"value": self._render("line.region_id.name or ''")},
|
||||
"width": 28,
|
||||
},
|
||||
'invoice': {
|
||||
'header': {
|
||||
'type': 'string',
|
||||
'value': self._('Invoice'),
|
||||
},
|
||||
'line': {
|
||||
'value': self._render("line.invoice_id.number"),
|
||||
},
|
||||
'width': 18,
|
||||
"invoice": {
|
||||
"header": {"type": "string", "value": self._("Invoice")},
|
||||
"line": {"value": self._render("line.invoice_id.number")},
|
||||
"width": 18,
|
||||
},
|
||||
}
|
||||
template.update(declaration._xls_template())
|
||||
@@ -198,80 +151,86 @@ class IntrastatProductDeclarationXlsx(models.AbstractModel):
|
||||
|
||||
def _get_ws_params(self, wb, data, declaration):
|
||||
template = self._get_template(declaration)
|
||||
if self.env.context.get('computation_lines'):
|
||||
if self.env.context.get("computation_lines"):
|
||||
wl = declaration._xls_computation_line_fields()
|
||||
report = 'computation'
|
||||
report = "computation"
|
||||
else:
|
||||
wl = declaration._xls_declaration_line_fields()
|
||||
report = 'declaration'
|
||||
report = "declaration"
|
||||
|
||||
title = self._get_title(declaration, report, title_format='normal')
|
||||
title_short = self._get_title(declaration, report,
|
||||
title_format='short')
|
||||
sheet_name = title_short[:31].replace('/', '-')
|
||||
title = self._get_title(declaration, report, title_format="normal")
|
||||
title_short = self._get_title(declaration, report, title_format="short")
|
||||
sheet_name = title_short[:31].replace("/", "-")
|
||||
|
||||
params = {
|
||||
'ws_name': sheet_name,
|
||||
'generate_ws_method': '_intrastat_report',
|
||||
'title': title,
|
||||
'wanted_list': wl,
|
||||
'col_specs': template,
|
||||
"ws_name": sheet_name,
|
||||
"generate_ws_method": "_intrastat_report",
|
||||
"title": title,
|
||||
"wanted_list": wl,
|
||||
"col_specs": template,
|
||||
}
|
||||
return [params]
|
||||
|
||||
def _get_title(self, declaration, report, title_format='normal'):
|
||||
def _get_title(self, declaration, report, title_format="normal"):
|
||||
title = declaration.year_month
|
||||
if title_format == 'normal':
|
||||
if report == 'computation':
|
||||
title += ' : ' + _('Computation Lines')
|
||||
if title_format == "normal":
|
||||
if report == "computation":
|
||||
title += " : " + _("Computation Lines")
|
||||
else:
|
||||
title += ' : ' + _('Declaration Lines')
|
||||
title += " : " + _("Declaration Lines")
|
||||
return title
|
||||
|
||||
def _report_title(self, ws, row_pos, ws_params, data, declaration):
|
||||
return self._write_ws_title(ws, row_pos, ws_params)
|
||||
|
||||
def _empty_report(self, ws, row_pos, ws_params, data, declaration,
|
||||
report):
|
||||
if report == 'computation':
|
||||
lines = _('Computation Lines')
|
||||
def _empty_report(self, ws, row_pos, ws_params, data, declaration, report):
|
||||
if report == "computation":
|
||||
lines = _("Computation Lines")
|
||||
else:
|
||||
lines = _('Declaration Lines')
|
||||
no_entries = _("No") + " " + lines + " " + _("for period %s") \
|
||||
% declaration.year_month
|
||||
lines = _("Declaration Lines")
|
||||
no_entries = (
|
||||
_("No") + " " + lines + " " + _("for period %s") % declaration.year_month
|
||||
)
|
||||
ws.write_string(row_pos, 0, no_entries, self.format_left_bold)
|
||||
|
||||
def _intrastat_report(self, workbook, ws, ws_params, data, declaration):
|
||||
|
||||
ws.set_landscape()
|
||||
ws.fit_to_pages(1, 0)
|
||||
ws.set_header(self.xls_headers['standard'])
|
||||
ws.set_footer(self.xls_footers['standard'])
|
||||
ws.set_header(self.xls_headers["standard"])
|
||||
ws.set_footer(self.xls_footers["standard"])
|
||||
|
||||
self._set_column_width(ws, ws_params)
|
||||
|
||||
row_pos = 0
|
||||
row_pos = self._report_title(ws, row_pos, ws_params, data, declaration)
|
||||
|
||||
if self.env.context.get('computation_lines'):
|
||||
report = 'computation'
|
||||
if self.env.context.get("computation_lines"):
|
||||
report = "computation"
|
||||
lines = declaration.computation_line_ids
|
||||
else:
|
||||
report = 'declaration'
|
||||
report = "declaration"
|
||||
lines = declaration.declaration_line_ids
|
||||
|
||||
if not lines:
|
||||
return self._empty_report(
|
||||
ws, row_pos, ws_params, data, declaration, report)
|
||||
return self._empty_report(ws, row_pos, ws_params, data, declaration, report)
|
||||
|
||||
row_pos = self._write_line(
|
||||
ws, row_pos, ws_params, col_specs_section='header',
|
||||
default_format=self.format_theader_yellow_left)
|
||||
ws,
|
||||
row_pos,
|
||||
ws_params,
|
||||
col_specs_section="header",
|
||||
default_format=self.format_theader_yellow_left,
|
||||
)
|
||||
|
||||
ws.freeze_panes(row_pos, 0)
|
||||
|
||||
for line in lines:
|
||||
row_pos = self._write_line(
|
||||
ws, row_pos, ws_params, col_specs_section='line',
|
||||
render_space={'line': line},
|
||||
default_format=self.format_tcell_left)
|
||||
ws,
|
||||
row_pos,
|
||||
ws_params,
|
||||
col_specs_section="line",
|
||||
render_space={"line": line},
|
||||
default_format=self.format_tcell_left,
|
||||
)
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
|
||||
<record id="invoice_form" model="ir.ui.view">
|
||||
<record id="view_move_form" model="ir.ui.view">
|
||||
<field name="name">intrastat.invoice.form</field>
|
||||
<field name="model">account.invoice</field>
|
||||
<field name="inherit_id" ref="account.invoice_form"/>
|
||||
<field name="model">account.move</field>
|
||||
<field name="inherit_id" ref="account.view_move_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//page[@name='other_info']//field[@name='name']" position="after">
|
||||
<xpath expr="//page[@name='other_info']//field[@name='invoice_incoterm_id']" position="after">
|
||||
<field name="intrastat_transaction_id"/>
|
||||
<field name="intrastat" invisible="1"/>
|
||||
<field name="intrastat_transport_id"
|
||||
@@ -21,6 +21,7 @@
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!--
|
||||
<record id="invoice_supplier_form" model="ir.ui.view">
|
||||
<field name="name">intrastat.invoice.supplier.form</field>
|
||||
<field name="model">account.invoice</field>
|
||||
@@ -44,5 +45,6 @@
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
-->
|
||||
|
||||
</odoo>
|
||||
@@ -87,7 +87,7 @@
|
||||
<field name="name">intrastat.product.declaration.tree</field>
|
||||
<field name="model">intrastat.product.declaration</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Intrastat Product Declarations" colors="blue:state=='draft'">
|
||||
<tree string="Intrastat Product Declarations" decoration-info="state=='draft'">
|
||||
<field name="year_month"/>
|
||||
<field name="revision"/>
|
||||
<field name="type"/>
|
||||
|
||||
@@ -10,60 +10,57 @@
|
||||
<odoo>
|
||||
|
||||
<!-- Intrastat Supplementary Unit -->
|
||||
<record id="intrastat_unit_form" model="ir.ui.view">
|
||||
<field name="name">intrastat.unit.form</field>
|
||||
<field name="model">intrastat.unit</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Intrastat Supplementary Unit">
|
||||
<div class="oe_button_box" name="button_box">
|
||||
<button name="toggle_active" type="object"
|
||||
class="oe_stat_button" icon="fa-archive">
|
||||
<field name="active" widget="boolean_button"
|
||||
options='{"terminology": "archive"}'/>
|
||||
</button>
|
||||
</div>
|
||||
<group name="main">
|
||||
<field name="name"/>
|
||||
<field name="uom_id" required="1"/>
|
||||
<field name="description"/>
|
||||
</group>
|
||||
</form>
|
||||
</field>
|
||||
<record id="intrastat_unit_form" model="ir.ui.view">
|
||||
<field name="name">intrastat.unit.form</field>
|
||||
<field name="model">intrastat.unit</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Intrastat Supplementary Unit">
|
||||
<field name="active" invisible="1"/>
|
||||
<widget name="web_ribbon" text="Archived"
|
||||
bg_color="bg-danger"
|
||||
attrs="{'invisible': [('active', '=', True)]}"/>
|
||||
<group name="main">
|
||||
<field name="name"/>
|
||||
<field name="uom_id" required="1"/>
|
||||
<field name="description"/>
|
||||
</group>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="intrastat_unit_tree" model="ir.ui.view">
|
||||
<field name="name">intrastat.unit.tree</field>
|
||||
<field name="model">intrastat.unit</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Intrastat Supplementary Units">
|
||||
<field name="name"/>
|
||||
<field name="uom_id"/>
|
||||
<field name="description"/>
|
||||
</tree>
|
||||
</field>
|
||||
<field name="name">intrastat.unit.tree</field>
|
||||
<field name="model">intrastat.unit</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Intrastat Supplementary Units">
|
||||
<field name="name"/>
|
||||
<field name="uom_id"/>
|
||||
<field name="description"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="intrastat_unit_search" model="ir.ui.view">
|
||||
<field name="name">intrastat.unit.search</field>
|
||||
<field name="model">intrastat.unit</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Search Intrastat Supplementary Units">
|
||||
<field name="name"
|
||||
filter_domain="['|', ('name', 'ilike', self), ('description', 'ilike', self)]"/>
|
||||
<filter string="Archived" name="inactive" domain="[('active', '=', False)]"/>
|
||||
<group string="Group By" name="groupby">
|
||||
<filter name="uom_groupby" string="Regular UoM"
|
||||
context="{'group_by': 'uom_id'}"/>
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
<field name="name">intrastat.unit.search</field>
|
||||
<field name="model">intrastat.unit</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Search Intrastat Supplementary Units">
|
||||
<field name="name"
|
||||
filter_domain="['|', ('name', 'ilike', self), ('description', 'ilike', self)]"/>
|
||||
<filter string="Archived" name="inactive" domain="[('active', '=', False)]"/>
|
||||
<group string="Group By" name="groupby">
|
||||
<filter name="uom_groupby" string="Regular UoM"
|
||||
context="{'group_by': 'uom_id'}"/>
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
|
||||
<record id="intrastat_unit_action" model="ir.actions.act_window">
|
||||
<field name="name">Supplementary Units</field>
|
||||
<field name="res_model">intrastat.unit</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="name">Supplementary Units</field>
|
||||
<field name="res_model">intrastat.unit</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
</record>
|
||||
|
||||
<menuitem id="intrastat_unit_menu"
|
||||
|
||||
Reference in New Issue
Block a user