Merge branch 'develop'
commit
935f10a97c
|
|
@ -28,6 +28,8 @@ class MaterialType(models.Model):
|
||||||
|
|
||||||
name = fields.Char(string='Bezeichnung', required=True)
|
name = fields.Char(string='Bezeichnung', required=True)
|
||||||
print_default_code = fields.Boolean(string='Drucke Artikelnummer', required=True, help='Definiert, ob die Artikelnummer gedruckt wird')
|
print_default_code = fields.Boolean(string='Drucke Artikelnummer', required=True, help='Definiert, ob die Artikelnummer gedruckt wird')
|
||||||
|
print_sales_pic = fields.Boolean(string='Drucke Artikelbild', default=True, required=False, help='Definiert, ob ein Artikelbild gedruckt wird (Angebot/Auftrag)')
|
||||||
|
print_production_pic = fields.Boolean(string='Drucke Produktionsbild', default=False, required=False, help='Definiert, ob ein Produktionsbild gedruckt wird (Produktionsauftrag)')
|
||||||
|
|
||||||
_sql_constraints = [
|
_sql_constraints = [
|
||||||
('name_uniq', 'unique(name)', 'Die Bezeichnung muss eindeutig sein')
|
('name_uniq', 'unique(name)', 'Die Bezeichnung muss eindeutig sein')
|
||||||
|
|
|
||||||
|
|
@ -67,6 +67,7 @@ class Partner(models.Model):
|
||||||
collective_bill = fields.Boolean(string='Sammelrechnung', default=True)
|
collective_bill = fields.Boolean(string='Sammelrechnung', default=True)
|
||||||
country_id = fields.Many2one('res.country', string='Country', ondelete='restrict', default=_default_country_id)
|
country_id = fields.Many2one('res.country', string='Country', ondelete='restrict', default=_default_country_id)
|
||||||
commission_account_ids = fields.Many2many(comodel_name='commission.account', string='Provisionsempfänger')
|
commission_account_ids = fields.Many2many(comodel_name='commission.account', string='Provisionsempfänger')
|
||||||
|
partner_flash = fields.Char()
|
||||||
|
|
||||||
_sql_constraints = [
|
_sql_constraints = [
|
||||||
('ref_uniq', 'unique(ref)', 'Die Interne Referenz muss eindeutig sein'),
|
('ref_uniq', 'unique(ref)', 'Die Interne Referenz muss eindeutig sein'),
|
||||||
|
|
@ -281,7 +282,7 @@ class Partner(models.Model):
|
||||||
common_list.extend(
|
common_list.extend(
|
||||||
['name', 'ref', 'partner_sector_id', 'info_uid', 'comment', 'vat', 'property_payment_term_id',
|
['name', 'ref', 'partner_sector_id', 'info_uid', 'comment', 'vat', 'property_payment_term_id',
|
||||||
'date_vat_check', 'active', 'property_product_pricelist', 'retail_partner_id',
|
'date_vat_check', 'active', 'property_product_pricelist', 'retail_partner_id',
|
||||||
'is_retailer', 'name2', 'carrier_id', 'collective_bill', 'fiscal_position'])
|
'is_retailer', 'name2', 'carrier_id', 'collective_bill', 'fiscal_position', 'partner_flash'])
|
||||||
return common_list
|
return common_list
|
||||||
elif self.env.context.get('sst_14', False):
|
elif self.env.context.get('sst_14', False):
|
||||||
common_list = ['firstname', 'lastname', 'midname', 'email', 'company_odoo_id', 'portal_id', 'opt_out']
|
common_list = ['firstname', 'lastname', 'midname', 'email', 'company_odoo_id', 'portal_id', 'opt_out']
|
||||||
|
|
|
||||||
|
|
@ -65,6 +65,8 @@ class SaleOrder(models.Model):
|
||||||
# pg9_call = fields.Char(string='PG9-Auftrag', compute='_pg9_call', store=False)
|
# pg9_call = fields.Char(string='PG9-Auftrag', compute='_pg9_call', store=False)
|
||||||
pg9_call_D = fields.Char(string='PG9-Auftrag_D', compute='_pg9_call', store=False)
|
pg9_call_D = fields.Char(string='PG9-Auftrag_D', compute='_pg9_call', store=False)
|
||||||
pg9_call_T = fields.Char(string='PG9-Auftrag_T', compute='_pg9_call', store=False)
|
pg9_call_T = fields.Char(string='PG9-Auftrag_T', compute='_pg9_call', store=False)
|
||||||
|
partner_flash = fields.Text(compute='_get_partner_flash', store=False)
|
||||||
|
# partner_flash = fields.Char(store=False)
|
||||||
|
|
||||||
@api.multi
|
@api.multi
|
||||||
def _reset_sequence(self):
|
def _reset_sequence(self):
|
||||||
|
|
@ -87,10 +89,16 @@ class SaleOrder(models.Model):
|
||||||
|
|
||||||
# end def _pg9_call
|
# end def _pg9_call
|
||||||
|
|
||||||
|
@api.multi
|
||||||
|
def _get_partner_flash(self):
|
||||||
|
for record in self:
|
||||||
|
record.partner_flash = record.partner_id.partner_flash
|
||||||
|
|
||||||
@api.multi
|
@api.multi
|
||||||
@api.onchange('partner_invoice_id')
|
@api.onchange('partner_invoice_id')
|
||||||
def _onchange_partner_invoice_id(self):
|
def _onchange_partner_invoice_id(self):
|
||||||
for record in self:
|
for record in self:
|
||||||
|
# record.partner_flash = record.partner_id.partner_flash
|
||||||
if record.partner_invoice_id.is_retailer:
|
if record.partner_invoice_id.is_retailer:
|
||||||
record.payment_term_id = record.partner_invoice_id.property_payment_term_id
|
record.payment_term_id = record.partner_invoice_id.property_payment_term_id
|
||||||
|
|
||||||
|
|
@ -452,6 +460,9 @@ class SaleOrder(models.Model):
|
||||||
@api.multi
|
@api.multi
|
||||||
@api.onchange('partner_id')
|
@api.onchange('partner_id')
|
||||||
def onchange_partner_id(self):
|
def onchange_partner_id(self):
|
||||||
|
# for record in self:
|
||||||
|
# record.partner_flash = record.partner_id.partner_flash
|
||||||
|
|
||||||
if not self.partner_id:
|
if not self.partner_id:
|
||||||
self.update({
|
self.update({
|
||||||
'partner_invoice_id': False,
|
'partner_invoice_id': False,
|
||||||
|
|
@ -472,6 +483,10 @@ class SaleOrder(models.Model):
|
||||||
|
|
||||||
if self.partner_id.team_id:
|
if self.partner_id.team_id:
|
||||||
values['team_id'] = self.partner_id.team_id.id
|
values['team_id'] = self.partner_id.team_id.id
|
||||||
|
|
||||||
|
if self.partner_id:
|
||||||
|
values['partner_flash'] = self.partner_id.partner_flash
|
||||||
|
|
||||||
self.update(values)
|
self.update(values)
|
||||||
|
|
||||||
@api.multi
|
@api.multi
|
||||||
|
|
@ -504,6 +519,14 @@ class SaleOrderLine(models.Model):
|
||||||
weight = fields.Float(string='Gewicht', compute='_compute_weight')
|
weight = fields.Float(string='Gewicht', compute='_compute_weight')
|
||||||
intrastat_id = fields.Many2one(comodel_name='report.intrastat.code', string='Intrastat Code')
|
intrastat_id = fields.Many2one(comodel_name='report.intrastat.code', string='Intrastat Code')
|
||||||
sequence = fields.Integer(string='Sequence', default=9999)
|
sequence = fields.Integer(string='Sequence', default=9999)
|
||||||
|
item_notes = fields.Text(string='Notes', related='lot_id.notes', store=False)
|
||||||
|
item_warn = fields.Boolean(string='Notes!!!', compute='_compute_item_warn', store=False)
|
||||||
|
|
||||||
|
@api.multi
|
||||||
|
def _compute_item_warn(self):
|
||||||
|
for record in self:
|
||||||
|
iNote = str(record.lot_id.notes)
|
||||||
|
record.item_warn = True if iNote.startswith('!') else False
|
||||||
|
|
||||||
@api.multi
|
@api.multi
|
||||||
@api.onchange('product_id')
|
@api.onchange('product_id')
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,9 @@
|
||||||
#
|
#
|
||||||
##############################################################################
|
##############################################################################
|
||||||
|
|
||||||
from odoo import fields, models, api, tools
|
from odoo import fields, models, api, tools, _
|
||||||
|
from odoo.exceptions import ValidationError
|
||||||
|
from odoo.tools.float_utils import float_compare, float_is_zero
|
||||||
|
|
||||||
|
|
||||||
class StockProductionLot(models.Model):
|
class StockProductionLot(models.Model):
|
||||||
|
|
@ -60,3 +62,67 @@ class StockPicking(models.Model):
|
||||||
|
|
||||||
res = lang_obj.format('%.' + str(2) + 'f', value, grouping=True, monetary=True)
|
res = lang_obj.format('%.' + str(2) + 'f', value, grouping=True, monetary=True)
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
|
||||||
|
class StockMove(models.Model):
|
||||||
|
_inherit = "stock.move"
|
||||||
|
|
||||||
|
manu_lots_visible = fields.Boolean(compute='_compute_manu_lots_visible')
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def create(self, vals):
|
||||||
|
res = super(StockMove, self).create(vals)
|
||||||
|
for move in res:
|
||||||
|
move.update({'sequence': move.sale_line_id.sequence,})
|
||||||
|
return res
|
||||||
|
|
||||||
|
@api.multi
|
||||||
|
def _prepare_move_line_vals(self, quantity=None, reserved_quant=None):
|
||||||
|
self.ensure_one()
|
||||||
|
vals = super(StockMove, self)._prepare_move_line_vals(quantity=quantity, reserved_quant=reserved_quant)
|
||||||
|
if self.sale_line_id.lot_id and not self.sale_line_id.product_id.can_be_sold_unconfigured:
|
||||||
|
move_lot = self.sale_line_id.lot_id.id
|
||||||
|
vals.update({'lot_id': move_lot,})
|
||||||
|
return vals
|
||||||
|
|
||||||
|
def _compute_manu_lots_visible(self):
|
||||||
|
for move_line in self:
|
||||||
|
if not move_line.product_id.can_be_sold_unconfigured:
|
||||||
|
move_line.manu_lots_visible = True
|
||||||
|
|
||||||
|
@api.multi
|
||||||
|
def action_show_lot(self):
|
||||||
|
self.ensure_one()
|
||||||
|
action = self.env.ref('stock.action_production_lot_form').read()[0]
|
||||||
|
action['res_id'] = self.move_line_ids.lot_id.id
|
||||||
|
action['view_mode'] = 'form'
|
||||||
|
action['views'] = [(False, 'form')]
|
||||||
|
|
||||||
|
return action
|
||||||
|
|
||||||
|
@api.depends('product_id', 'product_uom_qty', 'product_uom')
|
||||||
|
def _cal_move_weight(self):
|
||||||
|
for move in self:
|
||||||
|
if move.sale_line_id.lot_id and not move.product_id.can_be_sold_unconfigured:
|
||||||
|
move.weight += (move.product_qty * move.sale_line_id.lot_id.weight)
|
||||||
|
else:
|
||||||
|
move.weight += (move.product_qty * move.product_id.weight)
|
||||||
|
|
||||||
|
# print(move.weight)
|
||||||
|
|
||||||
|
|
||||||
|
class StockQuant(models.Model):
|
||||||
|
_inherit = "stock.quant"
|
||||||
|
|
||||||
|
@api.constrains('quantity')
|
||||||
|
def check_quantity(self):
|
||||||
|
info = ''
|
||||||
|
for quant in self:
|
||||||
|
if float_compare(quant.quantity, 1, precision_rounding=quant.product_uom_id.rounding) > 0 and quant.lot_id and quant.product_id.tracking == 'serial':
|
||||||
|
smls = self.env['stock.move.line'].search([('lot_id', '=', quant.lot_id.id)])
|
||||||
|
for sml in smls:
|
||||||
|
sm = self.env['stock.move'].search([('id', '=', sml.move_id.id)])
|
||||||
|
info += '\n %s; %s; %s: %s' % (sm.origin,sm.reference,sm.sequence,quant.lot_id.name)
|
||||||
|
|
||||||
|
if info:
|
||||||
|
raise ValidationError(_('A serial number should only be linked to a single product.') + info)
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,8 @@
|
||||||
<group>
|
<group>
|
||||||
<field name="name"/>
|
<field name="name"/>
|
||||||
<field name="print_default_code"/>
|
<field name="print_default_code"/>
|
||||||
|
<field name="print_sales_pic"/>
|
||||||
|
<field name="print_production_pic"/>
|
||||||
</group>
|
</group>
|
||||||
</form>
|
</form>
|
||||||
</field>
|
</field>
|
||||||
|
|
@ -21,6 +23,8 @@
|
||||||
<tree>
|
<tree>
|
||||||
<field name="name"/>
|
<field name="name"/>
|
||||||
<field name="print_default_code"/>
|
<field name="print_default_code"/>
|
||||||
|
<field name="print_sales_pic"/>
|
||||||
|
<field name="print_production_pic"/>
|
||||||
</tree>
|
</tree>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,9 @@
|
||||||
</xpath>
|
</xpath>
|
||||||
|
|
||||||
<xpath expr="//page[@name='internal_notes']" position="inside">
|
<xpath expr="//page[@name='internal_notes']" position="inside">
|
||||||
|
<label for="partner_flash">Flash: </label>
|
||||||
|
<field name="partner_flash"/>
|
||||||
|
<div class="oe_clear"/>
|
||||||
<group name="general">
|
<group name="general">
|
||||||
<group name="portal_info" string="Portal Info">
|
<group name="portal_info" string="Portal Info">
|
||||||
<field name="info_kundennr"/>
|
<field name="info_kundennr"/>
|
||||||
|
|
|
||||||
|
|
@ -6,12 +6,20 @@
|
||||||
<field name="model">sale.order</field>
|
<field name="model">sale.order</field>
|
||||||
<field name="inherit_id" ref="sale.view_order_form"/>
|
<field name="inherit_id" ref="sale.view_order_form"/>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
|
<tree position="attributes">
|
||||||
|
<!-- <attribute name="decoration-info">item_notes</attribute> -->
|
||||||
|
<attribute name="decoration-bf">item_warn</attribute>
|
||||||
|
</tree>
|
||||||
<xpath expr="//sheet" position="before">
|
<xpath expr="//sheet" position="before">
|
||||||
<header>
|
<header>
|
||||||
<button name="%(action_wizard_import_tzbox)d" string="TZBox-Datei importieren" type="action" class="oe_highlight oe_read_only"
|
<button name="%(action_wizard_import_tzbox)d" string="TZBox-Datei importieren" type="action" class="oe_highlight oe_read_only"
|
||||||
attrs="{'invisible':['|','|',('state','!=','draft'),('order_type','!=','T'),('assembly_state','not in',[False,'import_failed'])]}"/>
|
attrs="{'invisible':['|','|',('state','!=','draft'),('order_type','!=','T'),('assembly_state','not in',[False,'import_failed'])]}"/>
|
||||||
</header>
|
</header>
|
||||||
</xpath>
|
</xpath>
|
||||||
|
<field name="partner_id" position="before">
|
||||||
|
<field name="partner_flash" string="Flash: " class="oe_highlight" attrs="{'invisible':[('partner_flash','=',False)]}"/>
|
||||||
|
<div class="oe_clear"/>
|
||||||
|
</field>
|
||||||
<field name="client_order_ref" position="replace"/>
|
<field name="client_order_ref" position="replace"/>
|
||||||
<field name="origin" position="replace"/>
|
<field name="origin" position="replace"/>
|
||||||
<field name="payment_term_id" position="after">
|
<field name="payment_term_id" position="after">
|
||||||
|
|
@ -66,9 +74,29 @@
|
||||||
<field name="intrastat_id"/>
|
<field name="intrastat_id"/>
|
||||||
</xpath>
|
</xpath>
|
||||||
<xpath expr="//field[@name='order_line']/tree//field[@name='product_id']" position="after">
|
<xpath expr="//field[@name='order_line']/tree//field[@name='product_id']" position="after">
|
||||||
<field name="lot_id" options="{'no_open': True}"/>
|
<field name="item_notes" invisible="1"/>
|
||||||
|
<field name="item_warn" invisible="1"/>
|
||||||
|
<field name="lot_id" options='{"fg_color": "red:item_notes" }'/>
|
||||||
|
<!--
|
||||||
|
<button name="action_show_lot" string="Lot" type="object"
|
||||||
|
icon="fa-list"
|
||||||
|
attrs="{'invisible': ['|',('lot_id', '=', False),('item_notes', '>', '')]}" options='{"warn": true}'/>
|
||||||
|
<button name="action_show_lot" string="Notes" type="object"
|
||||||
|
icon="fa-exclamation-triangle"
|
||||||
|
attrs="{'invisible': ['|','|',('lot_id', '=', False),('item_notes', '=', False),('item_notes', '=', '')]}" options='{"warn": true}'/>
|
||||||
|
-->
|
||||||
|
<button name="action_show_lot" string="Lot" type="object"
|
||||||
|
icon="fa-list"
|
||||||
|
attrs="{'invisible': ['|',('lot_id', '=', False),('item_warn','=',True)]}" options='{"warn": true}'/>
|
||||||
|
<button name="action_show_lot" string="Notes" type="object"
|
||||||
|
icon="fa-exclamation-triangle"
|
||||||
|
attrs="{'invisible': ['|',('lot_id', '=', False),('item_warn','=',False)]}" options='{"warn": true}'/>
|
||||||
|
<!-- icon="{'fa-list': [('item_notes', '>', '')],'fa-exclamation-triangle': [('item_notes', '<', '')]}" -->
|
||||||
|
<!--
|
||||||
<button name="action_show_lot" string="Lot" type="object" icon="fa-list"
|
<button name="action_show_lot" string="Lot" type="object" icon="fa-list"
|
||||||
attrs="{'invisible': [('lot_id', '=', False)]}" options='{"warn": true}'/>
|
attrs="{'invisible': [('lot_id', '=', False)]}"
|
||||||
|
options='{"warn": true}'/>
|
||||||
|
-->
|
||||||
<field name="from_designbox"/>
|
<field name="from_designbox"/>
|
||||||
</xpath>
|
</xpath>
|
||||||
<xpath expr="//field[@name='order_line']/kanban//field[@name='product_id']" position="after">
|
<xpath expr="//field[@name='order_line']/kanban//field[@name='product_id']" position="after">
|
||||||
|
|
@ -128,6 +156,7 @@
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<field name="partner_id" position="after">
|
<field name="partner_id" position="after">
|
||||||
<field name="partner_invoice_id"/>
|
<field name="partner_invoice_id"/>
|
||||||
|
<field name="partner_shipping_id" invisible="1"/>
|
||||||
</field>
|
</field>
|
||||||
<field name="name" position="after">
|
<field name="name" position="after">
|
||||||
<field name="order_type"/>
|
<field name="order_type"/>
|
||||||
|
|
@ -143,6 +172,7 @@
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<field name="partner_id" position="after">
|
<field name="partner_id" position="after">
|
||||||
<field name="partner_invoice_id"/>
|
<field name="partner_invoice_id"/>
|
||||||
|
<field name="partner_shipping_id" invisible="1"/>
|
||||||
</field>
|
</field>
|
||||||
<field name="name" position="after">
|
<field name="name" position="after">
|
||||||
<field name="order_type"/>
|
<field name="order_type"/>
|
||||||
|
|
@ -162,9 +192,19 @@
|
||||||
<field name="name" position="after">
|
<field name="name" position="after">
|
||||||
<field name="client_order_ref"/>
|
<field name="client_order_ref"/>
|
||||||
<field name="origin"/>
|
<field name="origin"/>
|
||||||
<field name="assembly_state" string="PG Status"/>
|
<field name="confirmation_nr"/>
|
||||||
|
<field name="assembly_state"/>
|
||||||
<field name="order_type"/>
|
<field name="order_type"/>
|
||||||
</field>
|
</field>
|
||||||
|
<!--
|
||||||
|
<field name="partner_id" position="after">
|
||||||
|
<field name="partner_shipping_id"/>
|
||||||
|
<field name="partner_invoice_id"/>
|
||||||
|
</field>
|
||||||
|
-->
|
||||||
|
<field name="partner_id" position="replace">
|
||||||
|
<field name="partner_id" string="Kunde" filter_domain="['|','|',('partner_id','ilike',self),('partner_shipping_id','ilike',self),('partner_invoice_id','ilike',self)]"/>
|
||||||
|
</field>
|
||||||
<filter name="customer" position="after">
|
<filter name="customer" position="after">
|
||||||
<filter string="PG Status" domain="[]" context="{'group_by':'assembly_state'}"/>
|
<filter string="PG Status" domain="[]" context="{'group_by':'assembly_state'}"/>
|
||||||
<filter string="Rechnungsadresse" domain="[]" context="{'group_by':'partner_invoice_id'}"/>
|
<filter string="Rechnungsadresse" domain="[]" context="{'group_by':'partner_invoice_id'}"/>
|
||||||
|
|
|
||||||
|
|
@ -22,4 +22,17 @@
|
||||||
<field name="priority">0</field>
|
<field name="priority">0</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
|
<record id="view_delivery_manu_lot" model="ir.ui.view">
|
||||||
|
<field name="name">view_delivery_manu_lot</field>
|
||||||
|
<field name="model">stock.picking</field>
|
||||||
|
<field name="inherit_id" ref="stock.view_picking_form"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//field[@name='move_lines']/tree//field[@name='product_id']" position="after">
|
||||||
|
<field name="manu_lots_visible" invisible="1"/>
|
||||||
|
<button name="action_show_lot" string="Lot for manufactoring parts" type="object" icon="fa-list" attrs="{'invisible': [('manu_lots_visible', '=', False)]}" options="{"warn": true}"/>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
|
||||||
</odoo>
|
</odoo>
|
||||||
|
|
|
||||||
|
|
@ -45,8 +45,11 @@
|
||||||
</p>
|
</p>
|
||||||
<p t-if="o._name == 'sale.order'">
|
<p t-if="o._name == 'sale.order'">
|
||||||
<t t-if="not (env.context.get('proforma', False) or is_pro_forma)">
|
<t t-if="not (env.context.get('proforma', False) or is_pro_forma)">
|
||||||
<span t-if="o.state not in ['draft','sent']">Auftragsbestätigung</span>
|
<!--
|
||||||
<span t-if="o.state in ['draft','sent']">Angebot</span>
|
-->
|
||||||
|
<span t-if="not data.get('tz_report_production', False) and o.state not in ['draft','sent']">Auftragsbestätigung</span>
|
||||||
|
<span t-if="not data.get('tz_report_production', False) and o.state in ['draft','sent']">Angebot</span>
|
||||||
|
<span t-if="data.get('tz_report_production',False)">Produktionsschein</span>
|
||||||
</t>
|
</t>
|
||||||
<t t-if="env.context.get('proforma', False) or is_pro_forma">
|
<t t-if="env.context.get('proforma', False) or is_pro_forma">
|
||||||
<span>Proformarechnung</span>
|
<span>Proformarechnung</span>
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,7 @@ class AccountInvoiceLine(models.Model):
|
||||||
)
|
)
|
||||||
|
|
||||||
lot_formatted_note = fields.Html(
|
lot_formatted_note = fields.Html(
|
||||||
|
comodel_name='stock.production.lot',
|
||||||
string='Formatted Note',
|
string='Formatted Note',
|
||||||
compute='_compute_line_lots',
|
compute='_compute_line_lots',
|
||||||
)
|
)
|
||||||
|
|
@ -57,9 +58,8 @@ class AccountInvoiceLine(models.Model):
|
||||||
for line in self:
|
for line in self:
|
||||||
if not line.order_line_ids:
|
if not line.order_line_ids:
|
||||||
return
|
return
|
||||||
line.prod_lot_ids = self.mapped(
|
line.prod_lot_ids = line.mapped(
|
||||||
'order_line_ids.move_ids.move_line_ids.lot_id')
|
'order_line_ids.move_ids.move_line_ids.lot_id')
|
||||||
print('End')
|
|
||||||
|
|
||||||
@api.multi
|
@api.multi
|
||||||
def _compute_line_lots(self):
|
def _compute_line_lots(self):
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,11 @@
|
||||||
<span t-esc="o.partner_id.city"/>
|
<span t-esc="o.partner_id.city"/>
|
||||||
<br/>
|
<br/>
|
||||||
<span t-esc="o.partner_id.country_id.name"/>
|
<span t-esc="o.partner_id.country_id.name"/>
|
||||||
|
<br/>
|
||||||
|
<div>
|
||||||
|
<strong>Tel: </strong>
|
||||||
|
<span t-esc="o.partner_id.phone"/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-xs-3">
|
<div class="col-xs-3">
|
||||||
|
|
@ -41,6 +46,11 @@
|
||||||
<span t-esc="o.sale_id.partner_id.city"/>
|
<span t-esc="o.sale_id.partner_id.city"/>
|
||||||
<br/>
|
<br/>
|
||||||
<span t-esc="o.sale_id.partner_id.country_id.name"/>
|
<span t-esc="o.sale_id.partner_id.country_id.name"/>
|
||||||
|
<br/>
|
||||||
|
<div>
|
||||||
|
<strong>Tel: </strong>
|
||||||
|
<span t-esc="o.sale_id.partner_id.phone"/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-xs-3">
|
<div class="col-xs-3">
|
||||||
|
|
@ -101,6 +111,7 @@
|
||||||
<td t-if="o.origin">
|
<td t-if="o.origin">
|
||||||
<span t-field="o.origin"/>
|
<span t-field="o.origin"/>
|
||||||
</td>
|
</td>
|
||||||
|
<!--
|
||||||
<td name="td_sched_date">
|
<td name="td_sched_date">
|
||||||
<t t-if="o.state == 'done'">
|
<t t-if="o.state == 'done'">
|
||||||
<span t-field="o.date_done"/>
|
<span t-field="o.date_done"/>
|
||||||
|
|
@ -109,6 +120,10 @@
|
||||||
<span t-field="o.scheduled_date"/>
|
<span t-field="o.scheduled_date"/>
|
||||||
</t>
|
</t>
|
||||||
</td>
|
</td>
|
||||||
|
-->
|
||||||
|
<td name="td_sched_date">
|
||||||
|
<span t-field="o.scheduled_date" t-field-options='{"widget": "date"}'/>
|
||||||
|
</td>
|
||||||
<td t-if="o.picking_type_id.code == 'outgoing' and o.carrier_id">
|
<td t-if="o.picking_type_id.code == 'outgoing' and o.carrier_id">
|
||||||
<span t-field="o.carrier_id"/>
|
<span t-field="o.carrier_id"/>
|
||||||
</td>
|
</td>
|
||||||
|
|
@ -161,10 +176,14 @@
|
||||||
<th>
|
<th>
|
||||||
<strong>Product</strong>
|
<strong>Product</strong>
|
||||||
</th>
|
</th>
|
||||||
<th name="lot_serial" t-if="has_serial_number">
|
<!-- <th name="lot_serial" t-if="has_serial_number"> -->
|
||||||
|
<th class="text-center" name="lot_serial" t-if="has_serial_number and any([move_line.product_id.can_be_sold_unconfigured for move_line in o.move_line_ids])">
|
||||||
Lot/Serial Number
|
Lot/Serial Number
|
||||||
</th>
|
</th>
|
||||||
<th class="text-center">
|
<th class="text-center">
|
||||||
|
<strong>Weight</strong>
|
||||||
|
</th>
|
||||||
|
<th class="text-right">
|
||||||
<strong>Ordered Quantity</strong>
|
<strong>Ordered Quantity</strong>
|
||||||
</th>
|
</th>
|
||||||
<th t-if="any([move_line.state == 'done' for move_line in o.move_line_ids])"
|
<th t-if="any([move_line.state == 'done' for move_line in o.move_line_ids])"
|
||||||
|
|
@ -193,18 +212,15 @@
|
||||||
<span t-field="move_line.package_id"/>
|
<span t-field="move_line.package_id"/>
|
||||||
</t>
|
</t>
|
||||||
</td>
|
</td>
|
||||||
<t t-if="has_serial_number">
|
<t t-if="has_serial_number and any([move_line.product_id.can_be_sold_unconfigured for move_line in o.move_line_ids])">
|
||||||
<td>
|
<td class="text-center">
|
||||||
<table width="100%">
|
<table width="100%">
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<span t-field="move_line.lot_id"/>
|
<span t-field="move_line.lot_id" t-if="move_line.product_id.can_be_sold_unconfigured"/>
|
||||||
<t t-if="not move_line.lot_id">
|
|
||||||
<span t-field="move_line.lot_name"/>
|
|
||||||
</t>
|
|
||||||
</td>
|
</td>
|
||||||
<td name="lot_qty">
|
<td name="lot_qty">
|
||||||
<t t-if="move_line.product_qty">
|
<t t-if="move_line.product_qty and move_line.product_id.can_be_sold_unconfigured">
|
||||||
<span t-esc="o._formatLang(move_line.product_qty).strip('0').strip(',').strip('.')"/>
|
<span t-esc="o._formatLang(move_line.product_qty).strip('0').strip(',').strip('.')"/>
|
||||||
</t>
|
</t>
|
||||||
</td>
|
</td>
|
||||||
|
|
@ -213,6 +229,16 @@
|
||||||
</td>
|
</td>
|
||||||
</t>
|
</t>
|
||||||
<td class="text-center">
|
<td class="text-center">
|
||||||
|
<t t-if="not move_line.lot_id or move_line.product_id.can_be_sold_unconfigured">
|
||||||
|
<span t-esc="move_line.ordered_qty*move_line.product_id.weight"/>
|
||||||
|
<!-- <span t-field="move_line.product_id.weight"/> -->
|
||||||
|
</t>
|
||||||
|
<t t-if="move_line.lot_id and not move_line.product_id.can_be_sold_unconfigured">
|
||||||
|
<!-- <t t-set="weight" t-value="move_line.ordered_qty*move_line.lot_id.weight"/> -->
|
||||||
|
<span t-field="move_line.lot_id.weight"/>
|
||||||
|
</t>
|
||||||
|
</td>
|
||||||
|
<td class="text-right">
|
||||||
<span t-field="move_line.package_id"/>
|
<span t-field="move_line.package_id"/>
|
||||||
<span t-if="move_line.package_id">:</span>
|
<span t-if="move_line.package_id">:</span>
|
||||||
<!--
|
<!--
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from . import models
|
||||||
|
#from . import wizards
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# noinspection PyStatementEffect
|
||||||
|
{
|
||||||
|
'name': 'TZ Report Production',
|
||||||
|
'category': 'Custom',
|
||||||
|
'version': '11.0.1.0.0',
|
||||||
|
'summary': 'Print Report for Production (Description & Pictures)',
|
||||||
|
'description': 'Print Report for Production (Description & Pictures)',
|
||||||
|
'author': 'TZAustria',
|
||||||
|
'website': 'https://www.tzaustria.at',
|
||||||
|
'support': 'andreas.osim@glaser-co.at',
|
||||||
|
'depends': [
|
||||||
|
'base',
|
||||||
|
'sale',
|
||||||
|
'dp_reports',
|
||||||
|
'sale_management',
|
||||||
|
'sale_comment_template',
|
||||||
|
'dp_line_comment_template',
|
||||||
|
],
|
||||||
|
'data': [
|
||||||
|
'reports/report_production.xml',
|
||||||
|
],
|
||||||
|
'installable': True,
|
||||||
|
'auto_install': False,
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from . import report_helper
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from odoo import api, models
|
||||||
|
|
||||||
|
class SaleOrder(models.AbstractModel):
|
||||||
|
_name = 'report.tz_reports_production.report_production'
|
||||||
|
_inherit = 'report.abstract_report'
|
||||||
|
_template = 'tz_reports_production.report_production'
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def get_report_values(self, docids, data=None):
|
||||||
|
model = 'sale.order'
|
||||||
|
docs = self.env[model].browse(docids)
|
||||||
|
data.update(tz_report_production=True)
|
||||||
|
return {
|
||||||
|
'doc_ids': docids,
|
||||||
|
'doc_model': model,
|
||||||
|
'docs': docs,
|
||||||
|
'data': data,
|
||||||
|
'field_set_in_lines': self._field_set_in_lines,
|
||||||
|
'formatLang': self._formatLang,
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,298 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
|
<odoo>
|
||||||
|
<!-- Sale Order Body -->
|
||||||
|
<template id="report_production_document">
|
||||||
|
<t t-call="web.external_layout">
|
||||||
|
<t t-set="o" t-value="o.with_context({'lang':o.partner_id.lang})"/>
|
||||||
|
<div class="page">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-xs-3">
|
||||||
|
<t t-call="dp_reports.partner_data"/>
|
||||||
|
</div>
|
||||||
|
<div class="col-xs-3">
|
||||||
|
<strong>Lieferadresse</strong>
|
||||||
|
<div class="partner-data">
|
||||||
|
<span t-esc="o.partner_shipping_id.name"/>
|
||||||
|
<br/>
|
||||||
|
<span t-esc="o.partner_shipping_id.street"/>
|
||||||
|
<br/>
|
||||||
|
<span t-esc="o.partner_shipping_id.zip"/>
|
||||||
|
<span t-esc="o.partner_shipping_id.city"/>
|
||||||
|
<br/>
|
||||||
|
<span t-esc="o.partner_shipping_id.country_id.name"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mt32"/>
|
||||||
|
<div class="row height-20">
|
||||||
|
<div class="col-xs-3" style="padding-right: 4px">
|
||||||
|
<strong>
|
||||||
|
<span class="col-xs-8" style="padding: 0px;width: 58%;">Kundennr.:</span>
|
||||||
|
</strong>
|
||||||
|
<span class="col-xs-4" style="padding: 0px;width: 42%;" t-field="o.partner_id.ref"/>
|
||||||
|
</div>
|
||||||
|
<strong>
|
||||||
|
<span class="col-xs-2">Ansprechpartner:</span>
|
||||||
|
</strong>
|
||||||
|
<span class="col-xs-3" t-field="o.user_id"/>
|
||||||
|
<strong>
|
||||||
|
<span class="col-xs-2">Positionen:</span>
|
||||||
|
</strong>
|
||||||
|
<span class="col-xs-2" t-field="o.positions"/>
|
||||||
|
</div>
|
||||||
|
<div class="row height-20">
|
||||||
|
<div class="col-xs-3" style="padding-right: 4px">
|
||||||
|
<strong>
|
||||||
|
<span class="col-xs-8" style="padding: 0px;width: 58%;">Angebotsdatum:</span>
|
||||||
|
</strong>
|
||||||
|
<span class="col-xs-4" style="padding: 0px;width: 42%;" t-field="o.date_order"
|
||||||
|
t-options='{"widget": "date"}'/>
|
||||||
|
</div>
|
||||||
|
<strong>
|
||||||
|
<span class="col-xs-2">Email:</span>
|
||||||
|
</strong>
|
||||||
|
<span class="col-xs-3" t-field="res_company.email"/>
|
||||||
|
<p t-if="o.origin">
|
||||||
|
<strong>
|
||||||
|
<span class="col-xs-2">Ref:</span>
|
||||||
|
</strong>
|
||||||
|
<span class="col-xs-2" t-field="o.origin"/>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-xs-3" style="padding-right: 4px">
|
||||||
|
<strong>
|
||||||
|
<span class="col-xs-8" style="padding: 0px;width: 58%;">Lieferdatum:</span>
|
||||||
|
</strong>
|
||||||
|
<span class="col-xs-4" style="padding: 0px;width: 42%;" t-field="o.earliest_scheduled_date"
|
||||||
|
t-options='{"widget": "date"}'/>
|
||||||
|
</div>
|
||||||
|
<strong>
|
||||||
|
<span class="col-xs-2">Telefon:</span>
|
||||||
|
</strong>
|
||||||
|
<span class="col-xs-3" t-field="o.user_id.phone"/>
|
||||||
|
<strong>
|
||||||
|
<span class="col-xs-2">Bezeichnung:</span>
|
||||||
|
</strong>
|
||||||
|
<span class="col-xs-2" t-field="o.client_order_ref"/>
|
||||||
|
</div>
|
||||||
|
<div class="row mt32"/>
|
||||||
|
<p t-if="o.note1">
|
||||||
|
<span t-field="o.note1"/>
|
||||||
|
</p>
|
||||||
|
<table class="table table-condensed" style="background-color: #E6E7E9;">
|
||||||
|
<thead class="table-header">
|
||||||
|
<tr>
|
||||||
|
<th class="text-center">Pos.</th>
|
||||||
|
<th class="text-right"/>
|
||||||
|
<th class="text-left">Artikel</th>
|
||||||
|
<!--
|
||||||
|
<th class="text-right"/>
|
||||||
|
<th class="text-right">EP</th>
|
||||||
|
<th class="text-right" t-if="discount_is_set">Rabatt</th>
|
||||||
|
<th class="text-right">Gesamtpreis</th>
|
||||||
|
-->
|
||||||
|
<th class="text-right">Anzahl</th>
|
||||||
|
<th class="text-right">Gewicht</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody class="sale-tbody">
|
||||||
|
<t t-set="pos_nr" t-value="0"/>
|
||||||
|
<t t-foreach="o.order_line" t-as="order_line">
|
||||||
|
<tr>
|
||||||
|
<t t-set="pos_nr" t-value="pos_nr+1"/>
|
||||||
|
<td class="text-center">
|
||||||
|
<span t-esc="pos_nr"/>
|
||||||
|
</td>
|
||||||
|
<td class="text-right"/>
|
||||||
|
<td rowspan="2" class="text-left">
|
||||||
|
<t t-if="order_line.product_id.default_code and order_line.product_id.material_type_id.print_default_code">
|
||||||
|
<strong>
|
||||||
|
<span t-field="order_line.product_id.default_code"/>
|
||||||
|
</strong>
|
||||||
|
<br/>
|
||||||
|
</t>
|
||||||
|
<!--
|
||||||
|
<t t-if="order_line.intrastat_id">
|
||||||
|
<span>
|
||||||
|
<strong>Zolltarif Nr.:</strong>
|
||||||
|
<span t-field="order_line.intrastat_id"/>
|
||||||
|
</span>
|
||||||
|
<br/>
|
||||||
|
</t>
|
||||||
|
-->
|
||||||
|
<span t-field="order_line.name"/>
|
||||||
|
<!--
|
||||||
|
<t t-if="order_line.delivery_date and (order_line.delivery_date != o.delivery_date)">
|
||||||
|
<br/>
|
||||||
|
<strong>Voraussichtliches Lieferdatum:</strong>
|
||||||
|
<span t-field="order_line.delivery_date"/>
|
||||||
|
</t>
|
||||||
|
<p t-if="order_line.lot_id">
|
||||||
|
<span t-esc="order_line.lot_id.name.split('.')[2]"/>
|
||||||
|
</p>
|
||||||
|
-->
|
||||||
|
<p t-if="order_line.lot_id.notes">
|
||||||
|
<span t-field="order_line.lot_id.notes"/>
|
||||||
|
</p>
|
||||||
|
<p t-if="o.note_line">
|
||||||
|
<span t-field="o.note_line"/>
|
||||||
|
</p>
|
||||||
|
</td>
|
||||||
|
<td class="text-right">
|
||||||
|
<t t-if="order_line.product_uom_qty">
|
||||||
|
<span t-esc="o._formatLang(order_line.product_uom_qty, False).strip('0').strip(',').strip('.')"/>
|
||||||
|
</t>
|
||||||
|
</td>
|
||||||
|
<td class="text-right">
|
||||||
|
<t t-if="order_line.weight">
|
||||||
|
<span t-field="order_line.weight"/>
|
||||||
|
kg
|
||||||
|
</t>
|
||||||
|
</td>
|
||||||
|
<!--
|
||||||
|
<td rowspan="2" class="text-right">
|
||||||
|
<span t-if="order_line.hide_discount"
|
||||||
|
t-field="order_line.price_reduce"/>
|
||||||
|
<span t-if="not order_line.hide_discount"
|
||||||
|
t-field="order_line.price_unit"/>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td rowspan="2" class="text-right" t-if="discount_is_set">
|
||||||
|
<t t-if="not order_line.hide_discount and order_line.discount">
|
||||||
|
<span t-esc="o._formatLang(order_line.discount, False).strip('0').strip(',').strip('.')"/>
|
||||||
|
%
|
||||||
|
</t>
|
||||||
|
</td>
|
||||||
|
<td rowspan="2" class="text-right">
|
||||||
|
<span t-field="order_line.price_subtotal"/>
|
||||||
|
</td>
|
||||||
|
-->
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" style="border: 0;text-align: center;">
|
||||||
|
<img t-if="order_line.lot_id.image_medium and order_line.product_id.material_type_id.print_production_pic"
|
||||||
|
t-att-src="'data:image/png;base64,%s' % order_line.lot_id.image_medium.decode()" style="max-width: 128px;"/>
|
||||||
|
<img t-if="not order_line.lot_id.image_medium and order_line.product_id.image_medium and order_line.product_id.material_type_id.print_production_pic"
|
||||||
|
t-att-src="'data:image/png;base64,%s' % order_line.product_id.image_medium.decode()" style="max-width: 128px;"/>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</t>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-xs-4 pull-right">
|
||||||
|
<table class="table table-condensed">
|
||||||
|
<tbody class="totals-tbody">
|
||||||
|
<!--
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
Nettobetrag
|
||||||
|
</td>
|
||||||
|
<td class="text-right">
|
||||||
|
<span t-field="o.amount_untaxed"
|
||||||
|
t-options='{"widget": "monetary", "display_currency": o.currency_id}'/>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<span>USt.</span>
|
||||||
|
</td>
|
||||||
|
<td class="text-right">
|
||||||
|
<span t-field="o.amount_tax"
|
||||||
|
t-options='{"widget": "monetary", "display_currency": o.currency_id}'/>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr class="amount-total">
|
||||||
|
<td>
|
||||||
|
<strong>Gesamtsumme</strong>
|
||||||
|
</td>
|
||||||
|
<td class="text-right">
|
||||||
|
<strong>
|
||||||
|
<span t-field="o.amount_total"
|
||||||
|
t-options='{"widget": "monetary", "display_currency": o.currency_id}'/>
|
||||||
|
</strong>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
-->
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p t-if="o.note2">
|
||||||
|
<span t-field="o.note2"/>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
<div class="row">
|
||||||
|
<p class="col-xs-12">
|
||||||
|
<span t-field="o.note"/>
|
||||||
|
</p>
|
||||||
|
<p class="col-xs-12">
|
||||||
|
Gesamtgewicht:
|
||||||
|
<span t-esc="round(o.weight_total, 2)"/>
|
||||||
|
kg
|
||||||
|
<br/>
|
||||||
|
</p>
|
||||||
|
<p class="col-xs-12" t-if="o.incoterm.name">
|
||||||
|
Lieferkonditionen:
|
||||||
|
<span t-esc="o.incoterm.name"/>
|
||||||
|
<br/>
|
||||||
|
</p>
|
||||||
|
<p class="col-xs-12" t-if="o.payment_term_id and not o.partner_invoice_id.is_retailer">
|
||||||
|
Zahlungskonditionen:
|
||||||
|
<span t-esc="o.payment_term_id.name"/>
|
||||||
|
<br/>
|
||||||
|
</p>
|
||||||
|
<p class="col-xs-12" t-if="o.state not in ['draft','sent']">
|
||||||
|
Bitte prüfen Sie bei Anlieferung sofort die Verpackung bzw. Ware auf Beschädigung!
|
||||||
|
<br/>
|
||||||
|
Falls die beschädigte Ware angenommen wird, besteht kein Anspruch auf Kostenerstattung bzw.
|
||||||
|
Austausch.
|
||||||
|
</p>
|
||||||
|
<t t-if="o.state in ['draft','sent']">
|
||||||
|
<p class="col-xs-12">
|
||||||
|
Gültigkeit: 14 Tage ab Ausstellungsdatum
|
||||||
|
</p>
|
||||||
|
<p class="col-xs-12">
|
||||||
|
Auftrag erteilt am: ____________________
|
||||||
|
</p>
|
||||||
|
<p class="col-xs-12">
|
||||||
|
Unterschrift: ____________________
|
||||||
|
</p>
|
||||||
|
</t>
|
||||||
|
</div>
|
||||||
|
-->
|
||||||
|
</div>
|
||||||
|
</t>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template id="tz_reports_production.report_production">
|
||||||
|
<t t-call="web.html_container">
|
||||||
|
<t t-foreach="docs" t-as="o">
|
||||||
|
<t t-call="tz_reports_production.report_production_document" t-lang="o.partner_id.lang"/>
|
||||||
|
</t>
|
||||||
|
</t>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
<record id="tz_reports_production.action_report_production" model="ir.actions.report">
|
||||||
|
</record>
|
||||||
|
-->
|
||||||
|
<report
|
||||||
|
id="action_report_production"
|
||||||
|
string="Produktionschein"
|
||||||
|
model="sale.order"
|
||||||
|
report_type="qweb-pdf"
|
||||||
|
file="tz_reports_production.report_production"
|
||||||
|
name="tz_reports_production.report_production"
|
||||||
|
print_report_name="'Produktionsschein - %s' % (object.name)"
|
||||||
|
paperformat="dp_reports.paperformat_a4_european"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!-- print_report_name="(object.state in ('draft', 'sent') and 'Quotation - %s' % (object.name)) or 'Order - %s' % (object.name)" -->
|
||||||
|
|
||||||
|
</odoo>
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink"
|
||||||
|
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 361 KiB |
|
|
@ -0,0 +1,108 @@
|
||||||
|
.. image:: https://img.shields.io/badge/license-LGPL--3-blue.svg
|
||||||
|
:target: http://www.gnu.org/licenses/lgpl-3.0-standalone.html
|
||||||
|
:alt: License: LGPL-3
|
||||||
|
|
||||||
|
============================
|
||||||
|
Colorize field in tree views
|
||||||
|
============================
|
||||||
|
|
||||||
|
This module aims to add support for dynamically coloring fields in tree view
|
||||||
|
according to data in the record.
|
||||||
|
|
||||||
|
It provides attributes on fields with the similar syntax as the ``colors`` attribute
|
||||||
|
in tree tags.
|
||||||
|
|
||||||
|
Further, it provides a ``color_field`` attribute on tree tags's ``colors`` to use
|
||||||
|
a field's value as color.
|
||||||
|
|
||||||
|
Features
|
||||||
|
========
|
||||||
|
|
||||||
|
* Add attribute ``bg_color`` on field's ``options`` to color background of a cell in tree view
|
||||||
|
* Add attribute ``fg_color`` on field's ``options`` to change text color of a cell in tree view
|
||||||
|
* Add attribute ``color_field`` on the tree element's ``colors`` to use as color
|
||||||
|
|
||||||
|
Usage
|
||||||
|
=====
|
||||||
|
|
||||||
|
* In the tree view declaration, put ``options='{"bg_color": "red: customer==True"}`` attribute in the ``field`` tag::
|
||||||
|
|
||||||
|
...
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<tree string="View name">
|
||||||
|
...
|
||||||
|
<field name="name" options='{"bg_color": "red: customer == True"}'/>
|
||||||
|
...
|
||||||
|
</tree>
|
||||||
|
</field>
|
||||||
|
...
|
||||||
|
|
||||||
|
With this example, column which renders 'name' field will have its background colored in red.
|
||||||
|
|
||||||
|
* In the tree view declaration, put ``options='{"fg_color": "white:customer == True"}'`` attribute in the ``field`` tag::
|
||||||
|
|
||||||
|
...
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<tree string="View name">
|
||||||
|
...
|
||||||
|
<field name="name" options='{"fg_color": "white:customer == True"}'/>
|
||||||
|
...
|
||||||
|
</tree>
|
||||||
|
</field>
|
||||||
|
...
|
||||||
|
|
||||||
|
With this example, column which renders 'name' field will have its text colored in white on a customer records.
|
||||||
|
|
||||||
|
* In the tree view declaration, use ``options='"color_field": "my_color"'`` attribute in the ``tree`` tag::
|
||||||
|
|
||||||
|
...
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<tree string="View name" colors="color_field: my_color" >
|
||||||
|
...
|
||||||
|
<field name="my_color" invisible="1"/>
|
||||||
|
...
|
||||||
|
</tree>
|
||||||
|
</field>
|
||||||
|
...
|
||||||
|
|
||||||
|
With this example, the content of the field named `my_color` will be used to
|
||||||
|
populate the `my_color` CSS value. Use a function field to return whichever
|
||||||
|
color you want depending on the other record values. Note that this
|
||||||
|
overrides the rest of `colors` attributes, and that you need the tree
|
||||||
|
to load your field in the first place by adding it as invisible field.
|
||||||
|
|
||||||
|
**Note that you should always use single quotes for fields' ``options`` and wrap nested values in double quotes since ``options`` is a JSON object.**
|
||||||
|
|
||||||
|
Bug Tracker
|
||||||
|
===========
|
||||||
|
|
||||||
|
Bugs are tracked on `GitHub Issues
|
||||||
|
<https://github.com/OCA/web/issues>`_. In case of trouble, please
|
||||||
|
check there if your issue has already been reported. If you spotted it first,
|
||||||
|
help us smash it by providing a detailed and welcomed feedback.
|
||||||
|
|
||||||
|
Credits
|
||||||
|
=======
|
||||||
|
|
||||||
|
Contributors
|
||||||
|
------------
|
||||||
|
|
||||||
|
* Damien Crier <damien.crier@camptocamp.com>
|
||||||
|
* Holger Brunn <hbrunn@therp.nl>
|
||||||
|
* Artem Kostyuk <a.kostyuk@mobilunity.com>
|
||||||
|
* Guewen Baconnier <guewen.baconnier@camptocamp.com>
|
||||||
|
|
||||||
|
Maintainer
|
||||||
|
----------
|
||||||
|
|
||||||
|
.. image:: https://odoo-community.org/logo.png
|
||||||
|
:alt: Odoo Community Association
|
||||||
|
:target: https://odoo-community.org
|
||||||
|
|
||||||
|
This module is maintained by the OCA.
|
||||||
|
|
||||||
|
OCA, or the Odoo Community Association, is a nonprofit organization whose
|
||||||
|
mission is to support the collaborative development of Odoo features and
|
||||||
|
promote its widespread use.
|
||||||
|
|
||||||
|
To contribute to this module, please visit https://odoo-community.org.
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
# Copyright 2015-2018 Camptocamp SA, Damien Crier
|
||||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
# Copyright 2015-2018 Camptocamp SA, Damien Crier
|
||||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||||
|
{
|
||||||
|
'name': 'Colorize field in tree views',
|
||||||
|
'summary': 'Allows you to dynamically color fields on tree views',
|
||||||
|
'category': 'Hidden/Dependency',
|
||||||
|
'version': '11.0.1.0.1',
|
||||||
|
'depends': ['web'],
|
||||||
|
'author': "Camptocamp, Therp BV, Odoo Community Association (OCA)",
|
||||||
|
'license': 'AGPL-3',
|
||||||
|
'website': 'https://github.com/OCA/web',
|
||||||
|
'demo': [
|
||||||
|
"demo/res_users.xml",
|
||||||
|
],
|
||||||
|
'data': [
|
||||||
|
'views/web_tree_dynamic_colored_field.xml',
|
||||||
|
],
|
||||||
|
'installable': True,
|
||||||
|
}
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 9.2 KiB |
|
|
@ -0,0 +1,134 @@
|
||||||
|
odoo.define('web_tree_dynamic_colored_field', function (require) {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var ListRenderer = require('web.ListRenderer');
|
||||||
|
var pyeval = require('web.pyeval');
|
||||||
|
|
||||||
|
ListRenderer.include({
|
||||||
|
/**
|
||||||
|
* Look up for a `color_field` parameter in tree `colors` attribute
|
||||||
|
*
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
|
_renderBody: function () {
|
||||||
|
if (this.arch.attrs.colors) {
|
||||||
|
var colorAttr = this.arch.attrs.colors.split(';')
|
||||||
|
.filter(color => color.trim().startsWith('color_field'));
|
||||||
|
if (colorAttr.length > 0) {
|
||||||
|
var colorField = colorAttr[0].split(':')[1].trim();
|
||||||
|
// validate the presence of that field in tree view
|
||||||
|
var fieldNames = _(this.columns).map(
|
||||||
|
(value) => { return value.attrs.name; }
|
||||||
|
);
|
||||||
|
if (fieldNames.indexOf(colorField) === -1) {
|
||||||
|
console.warn(
|
||||||
|
"No field named '" + colorField + "' present in view."
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
this.colorField = colorField;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this._super();
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Colorize a cell during it's render
|
||||||
|
*
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
|
_renderBodyCell: function (record, node, colIndex, options) {
|
||||||
|
var $td = this._super.apply(this, arguments);
|
||||||
|
var ctx = this.getEvalContext(record);
|
||||||
|
this.applyColorize($td, record, node, ctx);
|
||||||
|
return $td;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Colorize the current cell depending on expressions provided.
|
||||||
|
*
|
||||||
|
* @param {Query Node} $td a <td> tag inside a table representing a list view
|
||||||
|
* @param {Object} node an XML node (must be a <field>)
|
||||||
|
*/
|
||||||
|
applyColorize: function ($td, record, node, ctx) {
|
||||||
|
// safely resolve value of `color_field` given in <tree>
|
||||||
|
var treeColor = record.data[this.colorField];
|
||||||
|
if (treeColor) {
|
||||||
|
$td.css('color', treeColor);
|
||||||
|
}
|
||||||
|
// apply <field>'s own `options`
|
||||||
|
if (!node.attrs.options) { return; }
|
||||||
|
if (node.tag !== 'field') { return; }
|
||||||
|
var nodeOptions = node.attrs.options;
|
||||||
|
if (!_.isObject(nodeOptions)) {
|
||||||
|
nodeOptions = pyeval.py_eval(nodeOptions);
|
||||||
|
}
|
||||||
|
this.applyColorizeHelper($td, nodeOptions, node, 'fg_color', 'color', ctx);
|
||||||
|
this.applyColorizeHelper($td, nodeOptions, node, 'bg_color', 'background-color', ctx);
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @param {Object} nodeOptions a mapping of nodeOptions parameters to the color itself
|
||||||
|
* @param {Object} node an XML node (must be a <field>)
|
||||||
|
* @param {string} nodeAttribute an attribute of a node to apply a style onto
|
||||||
|
* @param {string} cssAttribute a real CSS-compatible attribute
|
||||||
|
*/
|
||||||
|
applyColorizeHelper: function ($td, nodeOptions, node, nodeAttribute, cssAttribute, ctx) {
|
||||||
|
if (nodeOptions[nodeAttribute]) {
|
||||||
|
var colors = _(nodeOptions[nodeAttribute].split(';'))
|
||||||
|
.chain()
|
||||||
|
.map(this.pairColors)
|
||||||
|
.value()
|
||||||
|
.filter(function CheckUndefined(value, index, ar) {
|
||||||
|
return value !== undefined;
|
||||||
|
});
|
||||||
|
for (var i=0, len=colors.length; i<len; ++i) {
|
||||||
|
var pair = colors[i],
|
||||||
|
color = pair[0],
|
||||||
|
expression = pair[1];
|
||||||
|
if (py.evaluate(expression, ctx).toJSON()) {
|
||||||
|
$td.css(cssAttribute, color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse `<color>: <field> <operator> <value>` forms to
|
||||||
|
* evaluatable expressions
|
||||||
|
*
|
||||||
|
* @param {string} pairColor `color: expression` pair
|
||||||
|
*/
|
||||||
|
pairColors: function (pairColor) {
|
||||||
|
if (pairColor !== "") {
|
||||||
|
var pairList = pairColor.split(':'),
|
||||||
|
color = pairList[0],
|
||||||
|
// if one passes a bare color instead of an expression,
|
||||||
|
// then we consider that color is to be shown in any case
|
||||||
|
expression = pairList[1]? pairList[1] : 'True';
|
||||||
|
return [color, py.parse(py.tokenize(expression)), expression];
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Construct domain evaluation context, mostly by passing
|
||||||
|
* record's fields's values to local scope.
|
||||||
|
*
|
||||||
|
* @param {Object} record a record to build a context from
|
||||||
|
*/
|
||||||
|
getEvalContext: function (record) {
|
||||||
|
var ctx = _.extend(
|
||||||
|
{},
|
||||||
|
record.data,
|
||||||
|
pyeval.context()
|
||||||
|
);
|
||||||
|
for (var key in ctx) {
|
||||||
|
var value = ctx[key];
|
||||||
|
if (ctx[key] instanceof moment) {
|
||||||
|
// date/datetime fields are represented w/ Moment objects
|
||||||
|
// docs: https://momentjs.com/
|
||||||
|
ctx[key] = value.format('YYYY-MM-DD hh:mm:ss');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ctx;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<template id="assets_backend" name="web_tree_dynamic_colored_field assets" inherit_id="web.assets_backend">
|
||||||
|
<xpath expr="." position="inside">
|
||||||
|
<script type="text/javascript" src="/web_tree_dynamic_colored_field/static/src/js/web_tree_dynamic_colored_field.js"></script>
|
||||||
|
</xpath>
|
||||||
|
</template>
|
||||||
|
</odoo>
|
||||||
Loading…
Reference in New Issue