Merge branch '17.0' of github.com:Dur-Pro/bemade-addons into 17.0
This commit is contained in:
commit
61b555f78a
126 changed files with 2723 additions and 1096 deletions
33
.github/workflows/test.yml
vendored
Normal file
33
.github/workflows/test.yml
vendored
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
name: tests
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- "17.0-test-ci"
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- "17.0-test-ci"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
container: ghcr.io/bemade/test-odoo_arm64:latest
|
||||||
|
name: Test Repo Addons With Odoo
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
services:
|
||||||
|
postgres:
|
||||||
|
image: postgres:12.0
|
||||||
|
env:
|
||||||
|
POSTGRES_USER: odoo
|
||||||
|
POSTGRES_PASSWORD: odoo
|
||||||
|
POSTGRES_DB: odoo
|
||||||
|
ports:
|
||||||
|
- 5432:5432
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
persist-credientials: false
|
||||||
|
- name: Run Tests
|
||||||
|
run: run_tests.sh
|
||||||
|
|
||||||
1
account_credit_hold/__init__.py
Normal file
1
account_credit_hold/__init__.py
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
from . import models
|
||||||
21
account_credit_hold/__manifest__.py
Normal file
21
account_credit_hold/__manifest__.py
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
{
|
||||||
|
'name': 'Account Credit Hold',
|
||||||
|
'version': '17.0.1.0.0',
|
||||||
|
'summary': 'Allows setting clients on credit hold, blocking the ability confirm a new sales order.',
|
||||||
|
'description': 'Allows setting clients on hold, blocking the ability confirm a new sales order.',
|
||||||
|
'category': 'Accounting/Accounting',
|
||||||
|
'author': 'Bemade Inc.',
|
||||||
|
'maintainer': 'Marc Durepos <marc@bemade.org>',
|
||||||
|
'website': 'http://www.bemade.org',
|
||||||
|
'license': 'LGPL-3',
|
||||||
|
'depends': ['sale', 'account_followup', 'stock'],
|
||||||
|
'data': [
|
||||||
|
'views/account_followup_views.xml',
|
||||||
|
'views/sale_order_views.xml',
|
||||||
|
'views/res_partner_views.xml',
|
||||||
|
'views/stock_picking_views.xml',
|
||||||
|
],
|
||||||
|
'demo': [],
|
||||||
|
'installable': True,
|
||||||
|
'auto_install': False
|
||||||
|
}
|
||||||
5
account_credit_hold/models/__init__.py
Normal file
5
account_credit_hold/models/__init__.py
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
from . import account_followup
|
||||||
|
from . import res_partner
|
||||||
|
from . import sale_order
|
||||||
|
from . import stock_picking
|
||||||
|
from . import account_followup_report
|
||||||
8
account_credit_hold/models/account_followup.py
Normal file
8
account_credit_hold/models/account_followup.py
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
from odoo import fields, models, api
|
||||||
|
|
||||||
|
|
||||||
|
class FollowupLine(models.Model):
|
||||||
|
_inherit = 'account_followup.followup.line'
|
||||||
|
|
||||||
|
account_hold = fields.Boolean(string="Place on Credit Hold",
|
||||||
|
help="Place clients on account hold, restricting confirmation of new orders.")
|
||||||
12
account_credit_hold/models/account_followup_report.py
Normal file
12
account_credit_hold/models/account_followup_report.py
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
from odoo import models, fields, api, _
|
||||||
|
|
||||||
|
|
||||||
|
class FollowUpReport(models.AbstractModel):
|
||||||
|
_inherit = 'account.followup.report'
|
||||||
|
|
||||||
|
def _get_line_info(self, followup_line):
|
||||||
|
res = super()._get_line_info(followup_line)
|
||||||
|
res.update({
|
||||||
|
'credit_hold': followup_line.account_hold
|
||||||
|
})
|
||||||
|
return res
|
||||||
63
account_credit_hold/models/res_partner.py
Normal file
63
account_credit_hold/models/res_partner.py
Normal file
|
|
@ -0,0 +1,63 @@
|
||||||
|
from odoo import fields, models, api, _
|
||||||
|
from datetime import date
|
||||||
|
|
||||||
|
|
||||||
|
class Partner(models.Model):
|
||||||
|
_inherit = 'res.partner'
|
||||||
|
|
||||||
|
postpone_hold_until = fields.Date(string="Postpone Hold",
|
||||||
|
help="Grace period specific to this partner despite unpaid invoices.", )
|
||||||
|
|
||||||
|
hold_bg = fields.Boolean(string="Hold (technical)",
|
||||||
|
compute="_compute_hold_bg",
|
||||||
|
store=True,
|
||||||
|
default=False)
|
||||||
|
on_hold = fields.Boolean(string="Account on Hold",
|
||||||
|
help="Client account is on hold for unpaid overdue invoices.",
|
||||||
|
compute="_compute_on_hold")
|
||||||
|
|
||||||
|
@api.depends('postpone_hold_until', 'hold_bg')
|
||||||
|
def _compute_on_hold(self):
|
||||||
|
# manually re-compute hold_bg since followup_status doesn't get updated in Python but gets recalculated
|
||||||
|
# by an SQL query every time
|
||||||
|
self._compute_hold_bg()
|
||||||
|
for rec in self:
|
||||||
|
if rec.hold_bg and not (rec.postpone_hold_until and rec.postpone_hold_until > date.today()):
|
||||||
|
rec.on_hold = True
|
||||||
|
else:
|
||||||
|
if rec.on_hold:
|
||||||
|
rec.message_post(_("Credit hold lifted."))
|
||||||
|
rec.on_hold = False
|
||||||
|
|
||||||
|
@api.autovacuum
|
||||||
|
def _cleanup_expired_hold_postponements(self):
|
||||||
|
expired_holds = self.search([('postpone_hold_until', '<=', date.today())])
|
||||||
|
expired_holds.write({'postpone_hold_until': False})
|
||||||
|
|
||||||
|
def action_credit_hold(self):
|
||||||
|
for rec in self:
|
||||||
|
rec.hold_bg = True
|
||||||
|
rec.message_post(body=_('Placed on credit hold.'))
|
||||||
|
|
||||||
|
def action_lift_credit_hold(self):
|
||||||
|
for rec in self:
|
||||||
|
rec.hold_bg = False
|
||||||
|
rec.message_post(body=_('Credit hold lifted.'))
|
||||||
|
|
||||||
|
def _execute_followup_partner(self):
|
||||||
|
res = super()._execute_followup_partner()
|
||||||
|
if self.followup_status == 'in_need_of_action':
|
||||||
|
if self.followup_line_id.account_hold:
|
||||||
|
self.action_credit_hold()
|
||||||
|
return res
|
||||||
|
|
||||||
|
@api.depends('followup_status', 'followup_line_id')
|
||||||
|
def _compute_hold_bg(self):
|
||||||
|
first_followup_level = self._get_first_followup_level()
|
||||||
|
for rec in self:
|
||||||
|
prev_hold_bg = rec.hold_bg
|
||||||
|
level = rec.followup_line_id
|
||||||
|
if rec.followup_status == 'no_action_needed' and level == first_followup_level:
|
||||||
|
rec.hold_bg = False
|
||||||
|
else:
|
||||||
|
rec.hold_bg = prev_hold_bg
|
||||||
17
account_credit_hold/models/sale_order.py
Normal file
17
account_credit_hold/models/sale_order.py
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
from odoo import fields, models, api, _
|
||||||
|
from odoo.exceptions import UserError
|
||||||
|
|
||||||
|
|
||||||
|
class SaleOrder(models.Model):
|
||||||
|
_inherit = "sale.order"
|
||||||
|
|
||||||
|
client_on_hold = fields.Boolean(string='Client on Hold',
|
||||||
|
help="Whether or not a client has been put on hold due to unpaid invoices.",
|
||||||
|
related="partner_id.on_hold")
|
||||||
|
|
||||||
|
@api.depends('client_on_hold')
|
||||||
|
def action_confirm(self):
|
||||||
|
if any(self.mapped('client_on_hold')):
|
||||||
|
raise UserError(_("This client is on credit hold. No new orders can be confirmed until past-due invoices "
|
||||||
|
"are paid or the accounting team postpones the hold."))
|
||||||
|
super().action_confirm()
|
||||||
9
account_credit_hold/models/stock_picking.py
Normal file
9
account_credit_hold/models/stock_picking.py
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
from odoo import fields, models, api
|
||||||
|
|
||||||
|
|
||||||
|
class ModelName(models.Model):
|
||||||
|
_inherit = "stock.picking"
|
||||||
|
|
||||||
|
client_on_hold = fields.Boolean(string='Client on Hold',
|
||||||
|
help="Whether or not a client has been put on hold due to unpaid invoices.",
|
||||||
|
related="partner_id.on_hold")
|
||||||
35
account_credit_hold/readme.md
Normal file
35
account_credit_hold/readme.md
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
# Overview
|
||||||
|
|
||||||
|
This module adds the notion of placing clients on credit hold to the followup levels from the Odoo Enterprise
|
||||||
|
account_followup module. It adds an option to followup levels to mark clients matching the followup criteria as on
|
||||||
|
credit hold. This hold restricts the confirmation of new sales orders for these clients.
|
||||||
|
|
||||||
|
Accountant and admin users can set a date until which the account hold will be
|
||||||
|
postponed on a specific partner's form view. This effectively gives clients an extra
|
||||||
|
grace period, allowing orders to be confirmed until the period ends.
|
||||||
|
|
||||||
|
# Change Log
|
||||||
|
## 15.0.2.0.0 (2023-05-04)
|
||||||
|
|
||||||
|
Complete remake of the module, making the "Credit Hold" an action that is either manually or
|
||||||
|
automatically triggered from the Accounting > Followup Reports section or by setting the automatic application field
|
||||||
|
on followup levels.
|
||||||
|
|
||||||
|
## 15.0.1.1.0 (2023-05-03)
|
||||||
|
|
||||||
|
Adds a ribbon to stock pickings for clients on hold, and therefore a dependency on stock.
|
||||||
|
|
||||||
|
## 15.0.1.0.2 (2023-05-03)
|
||||||
|
|
||||||
|
Fix to sale order view and sale order confirmation for clients not on hold.
|
||||||
|
|
||||||
|
## 15.0.1.0.1 (2023-05-03)
|
||||||
|
|
||||||
|
Fix clients on hold when status is "outstanding_invoices".
|
||||||
|
|
||||||
|
## 15.0.1.0.0 (2023-05-02) Initial Release
|
||||||
|
|
||||||
|
Initial release of the module, including a setting on follow-up levels to toggle placing on credit hold. Blocks
|
||||||
|
the confirmation of sales orders for clients on credit hold. Red "Credit Hold" banner appears on sales orders and
|
||||||
|
partner form view when a client is on credit hold. Credit hold can be postponed by setting the "Postpone Hold" field
|
||||||
|
on the partner form view.
|
||||||
52
account_credit_hold/views/account_followup_views.xml
Normal file
52
account_credit_hold/views/account_followup_views.xml
Normal file
|
|
@ -0,0 +1,52 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<data>
|
||||||
|
|
||||||
|
<record id="account_followup_followup_line_form_inherit" model="ir.ui.view">
|
||||||
|
<field name="name">account_credit_hold.account_followup_line.form</field>
|
||||||
|
<field name="model">account_followup.followup.line</field>
|
||||||
|
<field name="inherit_id" ref="account_followup.view_account_followup_followup_line_form"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//field[@name='send_email']" position="before">
|
||||||
|
<field name="account_hold"/>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="customer_statements_form_view_inherit" model="ir.ui.view">
|
||||||
|
<field name="name">customer.statements.form.view.inherit</field>
|
||||||
|
<field name="model">res.partner</field>
|
||||||
|
<field name="inherit_id" ref="account_followup.customer_statements_form_view"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//button[last()]" position="after">
|
||||||
|
<field name="hold_bg" invisible="1"/>
|
||||||
|
<button
|
||||||
|
string="Credit Hold"
|
||||||
|
type="object"
|
||||||
|
name="action_credit_hold"
|
||||||
|
class="button btn-secondary"
|
||||||
|
attrs="{'invisible': [('hold_bg', '=', True)]}"
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
string="Lift Credit Hold"
|
||||||
|
type="object"
|
||||||
|
name="action_lift_credit_hold"
|
||||||
|
class="button btn-secondary"
|
||||||
|
attrs="{'invisible': [('hold_bg', '=', False)]}"
|
||||||
|
/>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="action_credit_hold" model="ir.actions.server">
|
||||||
|
<field name="name">action_credit_hold</field>
|
||||||
|
<field name="model_id" ref="base.model_res_partner"/>
|
||||||
|
<field name="binding_model_id" ref="base.model_res_partner"/>
|
||||||
|
<field name="binding_view_types">list,form</field>
|
||||||
|
<field name="state">code</field>
|
||||||
|
<field name="code">
|
||||||
|
records.action_credit_hold()
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
33
account_credit_hold/views/res_partner_views.xml
Normal file
33
account_credit_hold/views/res_partner_views.xml
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<data>
|
||||||
|
|
||||||
|
<record id="res_partner_form_inherit" model="ir.ui.view">
|
||||||
|
<field name="name">account_credit_hold.res_partner.form</field>
|
||||||
|
<field name="model">res.partner</field>
|
||||||
|
<field name="inherit_id" ref="base.view_partner_form"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//div[@name='button_box']" position="before">
|
||||||
|
<field name="hold_bg" invisible="True" />
|
||||||
|
<field name="on_hold" invisible="True" />
|
||||||
|
<widget name="web_ribbon" title="Credit Hold"
|
||||||
|
bg_color="bg-danger" attrs="{'invisible': [('on_hold','=',False)]}"/>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
<record id="view_partner_property_form_inherit" model="ir.ui.view">
|
||||||
|
<field name="name">account_credit_hold.view_partner_property_form</field>
|
||||||
|
<field name="model">res.partner</field>
|
||||||
|
<field name="inherit_id" ref="account.view_partner_property_form"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//group[@name='banks']" position="before">
|
||||||
|
<group string="Credit Hold" >
|
||||||
|
<field name="postpone_hold_until"
|
||||||
|
groups="account.group_account_manager,account.group_account_user"
|
||||||
|
attrs="{'readonly': [('hold_bg','=',False), ('postpone_hold_until','=',False)]}"/>
|
||||||
|
</group>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
18
account_credit_hold/views/sale_order_views.xml
Normal file
18
account_credit_hold/views/sale_order_views.xml
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<data>
|
||||||
|
|
||||||
|
<record id="sale_order_form_inherit" model="ir.ui.view">
|
||||||
|
<field name="name">account_credit_hold.sale_order.form</field>
|
||||||
|
<field name="model">sale.order</field>
|
||||||
|
<field name="inherit_id" ref="sale.view_order_form"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//div[@name='button_box']" position="before">
|
||||||
|
<field name="client_on_hold" invisible="True" />
|
||||||
|
<widget name="web_ribbon" title="Credit Hold"
|
||||||
|
bg_color="bg-danger" attrs="{'invisible': [('client_on_hold','=',False)] }"/>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
20
account_credit_hold/views/stock_picking_views.xml
Normal file
20
account_credit_hold/views/stock_picking_views.xml
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<data>
|
||||||
|
|
||||||
|
<record id="stock_picking_form_inherit" model="ir.ui.view">
|
||||||
|
<field name="name">account_credit_hold.stock_picking.form</field>
|
||||||
|
<field name="model">stock.picking</field>
|
||||||
|
<field name="inherit_id" ref="stock.view_picking_form"/>
|
||||||
|
<field name="priority" eval="8"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//div[@name='button_box']" position="before">
|
||||||
|
<field name="client_on_hold" invisible="True" />
|
||||||
|
<widget name="web_ribbon" title="Credit Hold"
|
||||||
|
bg_color="bg-danger" attrs="{'invisible': [('client_on_hold','=',False)] }"/>
|
||||||
|
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
{
|
{
|
||||||
"name": "Change default when adding follower",
|
"name": "Change default when adding follower",
|
||||||
"version": "15.0.0.0.1",
|
"version": "17.0.0.0.1",
|
||||||
"category": "Extra Tools",
|
"category": "Extra Tools",
|
||||||
'summary': 'Change default when adding follower',
|
'summary': 'Change default when adding follower',
|
||||||
"description": """
|
"description": """
|
||||||
|
|
|
||||||
|
|
@ -5,4 +5,7 @@ from odoo import models, fields, api
|
||||||
class MailWizardInviteDefault(models.TransientModel):
|
class MailWizardInviteDefault(models.TransientModel):
|
||||||
_inherit = 'mail.wizard.invite'
|
_inherit = 'mail.wizard.invite'
|
||||||
|
|
||||||
send_mail = fields.Boolean(default=False, help="If true, an invitation email will be sent to the recipient")
|
send_mail = fields.Boolean(
|
||||||
|
default=False,
|
||||||
|
help="If true, an invitation email will be sent to the recipient"
|
||||||
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
{
|
{
|
||||||
"name": "Fetchmail Only on production environment",
|
"name": "Fetchmail Only on production environment",
|
||||||
"version": "15.0.0.0.1",
|
"version": "17.0.0.0.1",
|
||||||
"category": "Extra Tools",
|
"category": "Extra Tools",
|
||||||
'summary': 'Fetchmail Only on production environment',
|
'summary': 'Fetchmail Only on production environment',
|
||||||
"description": """
|
"description": """
|
||||||
|
|
@ -10,7 +10,7 @@
|
||||||
"author": "Bemade",
|
"author": "Bemade",
|
||||||
'website': 'https://www.bemade.org',
|
'website': 'https://www.bemade.org',
|
||||||
"depends": [
|
"depends": [
|
||||||
'fetchmail',
|
'mail',
|
||||||
],
|
],
|
||||||
"data": [
|
"data": [
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -1 +1,2 @@
|
||||||
from . import models
|
from . import models
|
||||||
|
from . import wizard
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@
|
||||||
########################################################################################
|
########################################################################################
|
||||||
{
|
{
|
||||||
'name': 'Improved Field Service Management',
|
'name': 'Improved Field Service Management',
|
||||||
'version': '15.0.1.0.0',
|
'version': '17.0.0.1.0',
|
||||||
'summary': 'Adds functionality necessary for managing field service operations at Durpro.',
|
'summary': 'Adds functionality necessary for managing field service operations at Durpro.',
|
||||||
'description': 'Adds functionality necessary for managing field service operations at Durpro.',
|
'description': 'Adds functionality necessary for managing field service operations at Durpro.',
|
||||||
'category': 'Services/Field Service',
|
'category': 'Services/Field Service',
|
||||||
|
|
@ -43,7 +43,6 @@
|
||||||
'mail',
|
'mail',
|
||||||
],
|
],
|
||||||
'data': [
|
'data': [
|
||||||
# BV: FOR MIGRATION
|
|
||||||
'data/fsm_data.xml',
|
'data/fsm_data.xml',
|
||||||
'views/task_template_views.xml',
|
'views/task_template_views.xml',
|
||||||
'views/equipment.xml',
|
'views/equipment.xml',
|
||||||
|
|
@ -53,17 +52,21 @@
|
||||||
'views/menus.xml',
|
'views/menus.xml',
|
||||||
'views/task_views.xml',
|
'views/task_views.xml',
|
||||||
'views/sale_order_views.xml',
|
'views/sale_order_views.xml',
|
||||||
# 'reports/worksheet_custom_report_templates.xml',
|
'reports/worksheet_custom_report_templates.xml',
|
||||||
# 'reports/worksheet_custom_reports.xml',
|
'reports/worksheet_custom_reports.xml',
|
||||||
|
'wizard/new_task_from_template.xml',
|
||||||
],
|
],
|
||||||
'assets': {
|
'assets': {
|
||||||
'web.assets_tests': [
|
|
||||||
'bemade_fsm/static/tests/tours/task_template_tour.js',
|
|
||||||
'bemade_fsm/static/tests/tours/equipment_tour.js',
|
|
||||||
'bemade_fsm/static/tests/tours/sale_order_tour.js',
|
|
||||||
],
|
|
||||||
'web.report_assets_common': [
|
'web.report_assets_common': [
|
||||||
'bemade_fsm/static/src/scss/bemade_fsm.scss'
|
'bemade_fsm/static/src/scss/bemade_fsm.scss'
|
||||||
|
],
|
||||||
|
'web.assets_backend': [
|
||||||
|
# BV: need to readd these files
|
||||||
|
# 'bemade_fsm/static/src/js/kanban_view.js',
|
||||||
|
# 'bemade_fsm/static/src/js/list_view.js',
|
||||||
|
],
|
||||||
|
'web.assets_qweb': [
|
||||||
|
'bemade_fsm/static/src/xml/project_view_buttons.xml',
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
'installable': True,
|
'installable': True,
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,8 @@
|
||||||
<record id="planning_project_stage_waiting_parts" model="project.task.type">
|
<record id="planning_project_stage_waiting_parts" model="project.task.type">
|
||||||
<field name="sequence">2</field>
|
<field name="sequence">2</field>
|
||||||
<field name="name">Waiting on Parts</field>
|
<field name="name">Waiting on Parts</field>
|
||||||
<field name="legend_blocked">Blocked</field>
|
<!-- BV: legend_blocked n'existe plus -->
|
||||||
|
<!-- BV: <field name="legend_blocked">Blocked</field>-->
|
||||||
<field name="fold" eval="False"/>
|
<field name="fold" eval="False"/>
|
||||||
<!-- <field name="is_closed" eval="False"/>-->
|
<!-- <field name="is_closed" eval="False"/>-->
|
||||||
<field name="project_ids" eval="[(4,ref('industry_fsm.fsm_project'))]"/>
|
<field name="project_ids" eval="[(4,ref('industry_fsm.fsm_project'))]"/>
|
||||||
|
|
@ -12,7 +13,7 @@
|
||||||
<record id="planning_project_stage_work_completed" model="project.task.type">
|
<record id="planning_project_stage_work_completed" model="project.task.type">
|
||||||
<field name="sequence">15</field>
|
<field name="sequence">15</field>
|
||||||
<field name="name">Work Executed</field>
|
<field name="name">Work Executed</field>
|
||||||
<field name="legend_blocked">Blocked</field>
|
<!-- BV: <field name="legend_blocked">Blocked</field>-->
|
||||||
<field name="fold" eval="False"/>
|
<field name="fold" eval="False"/>
|
||||||
<!-- <field name="is_closed" eval="False"/>-->
|
<!-- <field name="is_closed" eval="False"/>-->
|
||||||
<field name="project_ids" eval="[(4,ref('industry_fsm.fsm_project'))]"/>
|
<field name="project_ids" eval="[(4,ref('industry_fsm.fsm_project'))]"/>
|
||||||
|
|
@ -20,7 +21,7 @@
|
||||||
<record id="planning_project_stage_exception" model="project.task.type">
|
<record id="planning_project_stage_exception" model="project.task.type">
|
||||||
<field name="sequence">19</field>
|
<field name="sequence">19</field>
|
||||||
<field name="name">Exception</field>
|
<field name="name">Exception</field>
|
||||||
<field name="legend_blocked">Blocked</field>
|
<!-- BV: <field name="legend_blocked">Blocked</field>-->
|
||||||
<field name="fold" eval="False"/>
|
<field name="fold" eval="False"/>
|
||||||
<!-- <field name="is_closed" eval="False"/>-->
|
<!-- <field name="is_closed" eval="False"/>-->
|
||||||
<field name="project_ids" eval="[(4,ref('industry_fsm.fsm_project'))]"/>
|
<field name="project_ids" eval="[(4,ref('industry_fsm.fsm_project'))]"/>
|
||||||
|
|
@ -35,8 +36,9 @@
|
||||||
<record id="industry_fsm.fsm_project" model="project.project">
|
<record id="industry_fsm.fsm_project" model="project.project">
|
||||||
<field name="type_ids"
|
<field name="type_ids"
|
||||||
eval="[(4, ref('industry_fsm.planning_project_stage_0')), (4, ref('industry_fsm.planning_project_stage_1')), (4, ref('industry_fsm.planning_project_stage_2')), (4, ref('planning_project_stage_work_completed')), (4, ref('industry_fsm.planning_project_stage_3')), (4, ref('industry_fsm.planning_project_stage_4'))]"/>
|
eval="[(4, ref('industry_fsm.planning_project_stage_0')), (4, ref('industry_fsm.planning_project_stage_1')), (4, ref('industry_fsm.planning_project_stage_2')), (4, ref('planning_project_stage_work_completed')), (4, ref('industry_fsm.planning_project_stage_3')), (4, ref('industry_fsm.planning_project_stage_4'))]"/>
|
||||||
<field name="allow_subtasks"
|
<!-- BV: allow_subtask n'existe plus-->
|
||||||
eval="True"/>
|
<!-- <field name="allow_subtasks"-->
|
||||||
|
<!-- eval="True"/>-->
|
||||||
</record>
|
</record>
|
||||||
<function model="ir.model.data" name="write">
|
<function model="ir.model.data" name="write">
|
||||||
<function name="search" model="ir.model.data">
|
<function name="search" model="ir.model.data">
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,8 @@ msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: Odoo Server 15.0+e\n"
|
"Project-Id-Version: Odoo Server 15.0+e\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2023-10-02 20:26+0000\n"
|
"POT-Creation-Date: 2024-02-13 17:09+0000\n"
|
||||||
"PO-Revision-Date: 2023-10-02 20:26+0000\n"
|
"PO-Revision-Date: 2024-02-13 17:09+0000\n"
|
||||||
"Last-Translator: \n"
|
"Last-Translator: \n"
|
||||||
"Language-Team: \n"
|
"Language-Team: \n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
|
|
@ -16,7 +16,7 @@ msgstr ""
|
||||||
"Plural-Forms: \n"
|
"Plural-Forms: \n"
|
||||||
|
|
||||||
#. module: bemade_fsm
|
#. module: bemade_fsm
|
||||||
#: model_terms:ir.ui.view,arch_db:bemade_fsm.workorder_page_sale_order_table
|
#: model_terms:ir.ui.view,arch_db:bemade_fsm.workorder_page_sale_order_table_with_pricing_and_labour
|
||||||
msgid ""
|
msgid ""
|
||||||
"<span groups=\"account.group_show_line_subtotals_tax_excluded\">\n"
|
"<span groups=\"account.group_show_line_subtotals_tax_excluded\">\n"
|
||||||
" Amount</span>\n"
|
" Amount</span>\n"
|
||||||
|
|
@ -24,9 +24,9 @@ msgid ""
|
||||||
" Total Price</span>"
|
" Total Price</span>"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"<span groups=\"account.group_show_line_subtotals_tax_excluded\">\n"
|
"<span groups=\"account.group_show_line_subtotals_tax_excluded\">\n"
|
||||||
" Prix</span>\n"
|
" Montant</span>\n"
|
||||||
" <span groups=\"account.group_show_line_subtotals_tax_included\">\n"
|
" <span groups=\"account.group_show_line_subtotals_tax_included\">\n"
|
||||||
" Prix</span>"
|
" Prix Total</span>"
|
||||||
|
|
||||||
#. module: bemade_fsm
|
#. module: bemade_fsm
|
||||||
#: model_terms:ir.ui.view,arch_db:bemade_fsm.workorder_page_signature_block
|
#: model_terms:ir.ui.view,arch_db:bemade_fsm.workorder_page_signature_block
|
||||||
|
|
@ -40,12 +40,12 @@ msgstr ""
|
||||||
" </span>"
|
" </span>"
|
||||||
|
|
||||||
#. module: bemade_fsm
|
#. module: bemade_fsm
|
||||||
#: model_terms:ir.ui.view,arch_db:bemade_fsm.workorder_page_sale_order_table
|
#: model_terms:ir.ui.view,arch_db:bemade_fsm.workorder_page_sale_order_table_with_pricing_and_labour
|
||||||
msgid "<span>Disc.%</span>"
|
msgid "<span>Disc.%</span>"
|
||||||
msgstr "<span>Escompte %</span>"
|
msgstr "<span>% Escompte</span>"
|
||||||
|
|
||||||
#. module: bemade_fsm
|
#. module: bemade_fsm
|
||||||
#: model_terms:ir.ui.view,arch_db:bemade_fsm.workorder_page_sale_order_table
|
#: model_terms:ir.ui.view,arch_db:bemade_fsm.workorder_page_sale_order_table_with_pricing_and_labour
|
||||||
msgid ""
|
msgid ""
|
||||||
"<strong class=\"mr16\">Section\n"
|
"<strong class=\"mr16\">Section\n"
|
||||||
" Subtotal</strong>"
|
" Subtotal</strong>"
|
||||||
|
|
@ -54,19 +54,19 @@ msgstr ""
|
||||||
" Sous-total</strong>"
|
" Sous-total</strong>"
|
||||||
|
|
||||||
#. module: bemade_fsm
|
#. module: bemade_fsm
|
||||||
#: model_terms:ir.ui.view,arch_db:bemade_fsm.workorder_page_sale_order_table
|
#: model_terms:ir.ui.view,arch_db:bemade_fsm.workorder_page_sale_order_table_with_pricing_and_labour
|
||||||
msgid "<strong>Taxes</strong>"
|
msgid "<strong>Taxes</strong>"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. module: bemade_fsm
|
#. module: bemade_fsm
|
||||||
#: model_terms:ir.ui.view,arch_db:bemade_fsm.workorder_page_sale_order_table
|
#: model_terms:ir.ui.view,arch_db:bemade_fsm.workorder_page_sale_order_table_with_pricing_and_labour
|
||||||
msgid "<strong>Total</strong>"
|
msgid "<strong>Total</strong>"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. module: bemade_fsm
|
#. module: bemade_fsm
|
||||||
#: model_terms:ir.ui.view,arch_db:bemade_fsm.workorder_page_sale_order_table
|
#: model_terms:ir.ui.view,arch_db:bemade_fsm.workorder_page_sale_order_table_with_pricing_and_labour
|
||||||
msgid "<strong>Untaxed amount</strong>"
|
msgid "<strong>Untaxed amount</strong>"
|
||||||
msgstr "<strong>Montant hors taxes</strong>"
|
msgstr "<strong>Montant hors-taxes</strong>"
|
||||||
|
|
||||||
#. module: bemade_fsm
|
#. module: bemade_fsm
|
||||||
#: model:ir.model.fields,field_description:bemade_fsm.field_bemade_fsm_equipment__message_needaction
|
#: model:ir.model.fields,field_description:bemade_fsm.field_bemade_fsm_equipment__message_needaction
|
||||||
|
|
@ -78,20 +78,15 @@ msgstr "Action requise"
|
||||||
msgid "Activities"
|
msgid "Activities"
|
||||||
msgstr "Activités"
|
msgstr "Activités"
|
||||||
|
|
||||||
#. module: bemade_fsm
|
|
||||||
#: model:ir.model.fields,field_description:bemade_fsm.field_bemade_fsm_equipment__activity_exception_decoration
|
|
||||||
msgid "Activity Exception Decoration"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#. module: bemade_fsm
|
#. module: bemade_fsm
|
||||||
#: model:ir.model.fields,field_description:bemade_fsm.field_bemade_fsm_equipment__activity_state
|
#: model:ir.model.fields,field_description:bemade_fsm.field_bemade_fsm_equipment__activity_state
|
||||||
msgid "Activity State"
|
msgid "Activity State"
|
||||||
msgstr ""
|
msgstr "État de l'activité"
|
||||||
|
|
||||||
#. module: bemade_fsm
|
#. module: bemade_fsm
|
||||||
#: model:ir.model.fields,field_description:bemade_fsm.field_bemade_fsm_equipment__activity_type_icon
|
#: model:ir.model.fields,field_description:bemade_fsm.field_bemade_fsm_equipment__activity_type_icon
|
||||||
msgid "Activity Type Icon"
|
msgid "Activity Type Icon"
|
||||||
msgstr ""
|
msgstr "Icône du type d'activité"
|
||||||
|
|
||||||
#. module: bemade_fsm
|
#. module: bemade_fsm
|
||||||
#: model:ir.model.fields,field_description:bemade_fsm.field_bemade_fsm_equipment__tag_ids
|
#: model:ir.model.fields,field_description:bemade_fsm.field_bemade_fsm_equipment__tag_ids
|
||||||
|
|
@ -119,14 +114,12 @@ msgstr "Bloqué"
|
||||||
#. module: bemade_fsm
|
#. module: bemade_fsm
|
||||||
#: model:ir.model.fields,field_description:bemade_fsm.field_project_task__allow_billable
|
#: model:ir.model.fields,field_description:bemade_fsm.field_project_task__allow_billable
|
||||||
msgid "Can be billed"
|
msgid "Can be billed"
|
||||||
msgstr ""
|
msgstr "Peut être facturé"
|
||||||
|
|
||||||
#. module: bemade_fsm
|
#. module: bemade_fsm
|
||||||
#: model:ir.model.fields,help:bemade_fsm.field_res_partner__is_site_contact
|
#: model_terms:ir.ui.view,arch_db:bemade_fsm.project_task_from_template_wizard_view_form
|
||||||
#: model:ir.model.fields,help:bemade_fsm.field_res_users__is_site_contact
|
msgid "Cancel"
|
||||||
msgid ""
|
msgstr "Annuler"
|
||||||
"Check if the contact is a site contact, otherwise it is a not in that list"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#. module: bemade_fsm
|
#. module: bemade_fsm
|
||||||
#: model:ir.model.fields,help:bemade_fsm.field_bemade_fsm_equipment__tag_ids
|
#: model:ir.model.fields,help:bemade_fsm.field_bemade_fsm_equipment__tag_ids
|
||||||
|
|
@ -178,11 +171,37 @@ msgstr ""
|
||||||
msgid "Contacts and Equipment"
|
msgid "Contacts and Equipment"
|
||||||
msgstr "Contacts et équipements"
|
msgstr "Contacts et équipements"
|
||||||
|
|
||||||
|
#. module: bemade_fsm
|
||||||
|
#: model_terms:ir.ui.view,arch_db:bemade_fsm.project_task_from_template_wizard_view_form
|
||||||
|
msgid "Create Task"
|
||||||
|
msgstr "Créer une tâche"
|
||||||
|
|
||||||
|
#. module: bemade_fsm
|
||||||
|
#. openerp-web
|
||||||
|
#: code:addons/bemade_fsm/static/src/xml/project_view_buttons.xml:0
|
||||||
|
#: code:addons/bemade_fsm/static/src/xml/project_view_buttons.xml:0
|
||||||
|
#, python-format
|
||||||
|
msgid "Create Task from Template"
|
||||||
|
msgstr "Créer une tâche à partir d'un gabarit"
|
||||||
|
|
||||||
|
#. module: bemade_fsm
|
||||||
|
#: model:ir.model,name:bemade_fsm.model_project_task_from_template_wizard
|
||||||
|
msgid "Create Task from Template Wizard"
|
||||||
|
msgstr "Assistant pour création de tâche à partir d'un gabarit"
|
||||||
|
|
||||||
|
#. module: bemade_fsm
|
||||||
|
#. openerp-web
|
||||||
|
#: code:addons/bemade_fsm/static/src/xml/project_view_buttons.xml:0
|
||||||
|
#, python-format
|
||||||
|
msgid "Create from Template"
|
||||||
|
msgstr "Créer à partir d'un gabarit"
|
||||||
|
|
||||||
#. module: bemade_fsm
|
#. module: bemade_fsm
|
||||||
#: model:ir.model.fields,field_description:bemade_fsm.field_bemade_fsm_equipment__create_uid
|
#: model:ir.model.fields,field_description:bemade_fsm.field_bemade_fsm_equipment__create_uid
|
||||||
#: model:ir.model.fields,field_description:bemade_fsm.field_bemade_fsm_equipment_tag__create_uid
|
#: model:ir.model.fields,field_description:bemade_fsm.field_bemade_fsm_equipment_tag__create_uid
|
||||||
#: model:ir.model.fields,field_description:bemade_fsm.field_bemade_fsm_equipment_type__create_uid
|
#: model:ir.model.fields,field_description:bemade_fsm.field_bemade_fsm_equipment_type__create_uid
|
||||||
#: model:ir.model.fields,field_description:bemade_fsm.field_bemade_fsm_visit__create_uid
|
#: model:ir.model.fields,field_description:bemade_fsm.field_bemade_fsm_visit__create_uid
|
||||||
|
#: model:ir.model.fields,field_description:bemade_fsm.field_project_task_from_template_wizard__create_uid
|
||||||
#: model:ir.model.fields,field_description:bemade_fsm.field_project_task_template__create_uid
|
#: model:ir.model.fields,field_description:bemade_fsm.field_project_task_template__create_uid
|
||||||
msgid "Created by"
|
msgid "Created by"
|
||||||
msgstr "Créé par"
|
msgstr "Créé par"
|
||||||
|
|
@ -192,6 +211,7 @@ msgstr "Créé par"
|
||||||
#: model:ir.model.fields,field_description:bemade_fsm.field_bemade_fsm_equipment_tag__create_date
|
#: model:ir.model.fields,field_description:bemade_fsm.field_bemade_fsm_equipment_tag__create_date
|
||||||
#: model:ir.model.fields,field_description:bemade_fsm.field_bemade_fsm_equipment_type__create_date
|
#: model:ir.model.fields,field_description:bemade_fsm.field_bemade_fsm_equipment_type__create_date
|
||||||
#: model:ir.model.fields,field_description:bemade_fsm.field_bemade_fsm_visit__create_date
|
#: model:ir.model.fields,field_description:bemade_fsm.field_bemade_fsm_visit__create_date
|
||||||
|
#: model:ir.model.fields,field_description:bemade_fsm.field_project_task_from_template_wizard__create_date
|
||||||
#: model:ir.model.fields,field_description:bemade_fsm.field_project_task_template__create_date
|
#: model:ir.model.fields,field_description:bemade_fsm.field_project_task_template__create_date
|
||||||
msgid "Created on"
|
msgid "Created on"
|
||||||
msgstr "Date de création"
|
msgstr "Date de création"
|
||||||
|
|
@ -206,6 +226,11 @@ msgstr "Client"
|
||||||
msgid "Customer:"
|
msgid "Customer:"
|
||||||
msgstr "Client :"
|
msgstr "Client :"
|
||||||
|
|
||||||
|
#. module: bemade_fsm
|
||||||
|
#: model_terms:ir.ui.view,arch_db:bemade_fsm.workorder_page_timesheet_entries
|
||||||
|
msgid "Date"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#. module: bemade_fsm
|
#. module: bemade_fsm
|
||||||
#: model:ir.model.fields,field_description:bemade_fsm.field_project_task_template__assignees
|
#: model:ir.model.fields,field_description:bemade_fsm.field_project_task_template__assignees
|
||||||
msgid "Default Assignees"
|
msgid "Default Assignees"
|
||||||
|
|
@ -247,7 +272,8 @@ msgid "Default tags for tasks created from this template."
|
||||||
msgstr "Étiquettes par défaut pour tâches créées à partir de ce gabarit."
|
msgstr "Étiquettes par défaut pour tâches créées à partir de ce gabarit."
|
||||||
|
|
||||||
#. module: bemade_fsm
|
#. module: bemade_fsm
|
||||||
#: model_terms:ir.ui.view,arch_db:bemade_fsm.workorder_page_sale_order_table
|
#: model_terms:ir.ui.view,arch_db:bemade_fsm.workorder_page_materials_table
|
||||||
|
#: model_terms:ir.ui.view,arch_db:bemade_fsm.workorder_page_sale_order_table_with_pricing_and_labour
|
||||||
msgid "Delivered"
|
msgid "Delivered"
|
||||||
msgstr "Livré"
|
msgstr "Livré"
|
||||||
|
|
||||||
|
|
@ -255,7 +281,8 @@ msgstr "Livré"
|
||||||
#: model:ir.model.fields,field_description:bemade_fsm.field_bemade_fsm_equipment__description
|
#: model:ir.model.fields,field_description:bemade_fsm.field_bemade_fsm_equipment__description
|
||||||
#: model:ir.model.fields,field_description:bemade_fsm.field_project_task_template__description
|
#: model:ir.model.fields,field_description:bemade_fsm.field_project_task_template__description
|
||||||
#: model_terms:ir.ui.view,arch_db:bemade_fsm.task_template_form_view
|
#: model_terms:ir.ui.view,arch_db:bemade_fsm.task_template_form_view
|
||||||
#: model_terms:ir.ui.view,arch_db:bemade_fsm.workorder_page_sale_order_table
|
#: model_terms:ir.ui.view,arch_db:bemade_fsm.workorder_page_materials_table
|
||||||
|
#: model_terms:ir.ui.view,arch_db:bemade_fsm.workorder_page_sale_order_table_with_pricing_and_labour
|
||||||
msgid "Description"
|
msgid "Description"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|
@ -274,6 +301,7 @@ msgstr "Détails"
|
||||||
#: model:ir.model.fields,field_description:bemade_fsm.field_bemade_fsm_equipment_tag__display_name
|
#: model:ir.model.fields,field_description:bemade_fsm.field_bemade_fsm_equipment_tag__display_name
|
||||||
#: model:ir.model.fields,field_description:bemade_fsm.field_bemade_fsm_equipment_type__display_name
|
#: model:ir.model.fields,field_description:bemade_fsm.field_bemade_fsm_equipment_type__display_name
|
||||||
#: model:ir.model.fields,field_description:bemade_fsm.field_bemade_fsm_visit__display_name
|
#: model:ir.model.fields,field_description:bemade_fsm.field_bemade_fsm_visit__display_name
|
||||||
|
#: model:ir.model.fields,field_description:bemade_fsm.field_project_task_from_template_wizard__display_name
|
||||||
#: model:ir.model.fields,field_description:bemade_fsm.field_project_task_template__display_name
|
#: model:ir.model.fields,field_description:bemade_fsm.field_project_task_template__display_name
|
||||||
msgid "Display Name"
|
msgid "Display Name"
|
||||||
msgstr "Nom pour affichage"
|
msgstr "Nom pour affichage"
|
||||||
|
|
@ -286,7 +314,7 @@ msgstr "Employés assignés aux tâches créées à partir de ce gabarit."
|
||||||
#. module: bemade_fsm
|
#. module: bemade_fsm
|
||||||
#: model:ir.model.fields,field_description:bemade_fsm.field_project_task__planned_date_end
|
#: model:ir.model.fields,field_description:bemade_fsm.field_project_task__planned_date_end
|
||||||
msgid "End Date"
|
msgid "End Date"
|
||||||
msgstr ""
|
msgstr "Date de fin"
|
||||||
|
|
||||||
#. module: bemade_fsm
|
#. module: bemade_fsm
|
||||||
#: model:ir.actions.act_window,name:bemade_fsm.action_window_equipment
|
#: model:ir.actions.act_window,name:bemade_fsm.action_window_equipment
|
||||||
|
|
@ -302,8 +330,9 @@ msgstr "Équipement désservi"
|
||||||
|
|
||||||
#. module: bemade_fsm
|
#. module: bemade_fsm
|
||||||
#: model:ir.model.fields,field_description:bemade_fsm.field_res_partner__equipment_count
|
#: model:ir.model.fields,field_description:bemade_fsm.field_res_partner__equipment_count
|
||||||
|
#: model:ir.model.fields,field_description:bemade_fsm.field_res_users__equipment_count
|
||||||
msgid "Equipment Count"
|
msgid "Equipment Count"
|
||||||
msgstr ""
|
msgstr "Nombre d'équipemenets"
|
||||||
|
|
||||||
#. module: bemade_fsm
|
#. module: bemade_fsm
|
||||||
#: model:ir.model.fields,field_description:bemade_fsm.field_bemade_fsm_equipment__complete_name
|
#: model:ir.model.fields,field_description:bemade_fsm.field_bemade_fsm_equipment__complete_name
|
||||||
|
|
@ -313,7 +342,7 @@ msgstr "Nom d'équipement"
|
||||||
#. module: bemade_fsm
|
#. module: bemade_fsm
|
||||||
#: model_terms:ir.ui.view,arch_db:bemade_fsm.workorder_equipment_summary
|
#: model_terms:ir.ui.view,arch_db:bemade_fsm.workorder_equipment_summary
|
||||||
msgid "Equipment Serviced"
|
msgid "Equipment Serviced"
|
||||||
msgstr "Équipement désservi"
|
msgstr "Équipement(s) désservi"
|
||||||
|
|
||||||
#. module: bemade_fsm
|
#. module: bemade_fsm
|
||||||
#: model:ir.actions.act_window,name:bemade_fsm.action_window_equipment_tags
|
#: model:ir.actions.act_window,name:bemade_fsm.action_window_equipment_tags
|
||||||
|
|
@ -349,11 +378,6 @@ msgstr "Durée estimée"
|
||||||
msgid "Exception"
|
msgid "Exception"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. module: bemade_fsm
|
|
||||||
#: model:ir.model.fields,field_description:bemade_fsm.field_bemade_fsm_equipment__failed_message_ids
|
|
||||||
msgid "Failed Messages"
|
|
||||||
msgstr "Messages échoués"
|
|
||||||
|
|
||||||
#. module: bemade_fsm
|
#. module: bemade_fsm
|
||||||
#: model_terms:ir.ui.view,arch_db:bemade_fsm.bemade_fsm_project_task_form_inherit
|
#: model_terms:ir.ui.view,arch_db:bemade_fsm.bemade_fsm_project_task_form_inherit
|
||||||
#: model_terms:ir.ui.view,arch_db:bemade_fsm.partner_equipment_location_view_form
|
#: model_terms:ir.ui.view,arch_db:bemade_fsm.partner_equipment_location_view_form
|
||||||
|
|
@ -389,7 +413,7 @@ msgstr "Abonnés (partenaires)"
|
||||||
#. module: bemade_fsm
|
#. module: bemade_fsm
|
||||||
#: model:ir.model.fields,help:bemade_fsm.field_bemade_fsm_equipment__activity_type_icon
|
#: model:ir.model.fields,help:bemade_fsm.field_bemade_fsm_equipment__activity_type_icon
|
||||||
msgid "Font awesome icon e.g. fa-tasks"
|
msgid "Font awesome icon e.g. fa-tasks"
|
||||||
msgstr ""
|
msgstr "Icône Font Awesome p.ex. fa-tasks"
|
||||||
|
|
||||||
#. module: bemade_fsm
|
#. module: bemade_fsm
|
||||||
#: model:ir.model.fields,field_description:bemade_fsm.field_sale_order_line__is_fully_delivered
|
#: model:ir.model.fields,field_description:bemade_fsm.field_sale_order_line__is_fully_delivered
|
||||||
|
|
@ -416,9 +440,10 @@ msgstr "A un message"
|
||||||
#: model:ir.model.fields,field_description:bemade_fsm.field_bemade_fsm_equipment_tag__id
|
#: model:ir.model.fields,field_description:bemade_fsm.field_bemade_fsm_equipment_tag__id
|
||||||
#: model:ir.model.fields,field_description:bemade_fsm.field_bemade_fsm_equipment_type__id
|
#: model:ir.model.fields,field_description:bemade_fsm.field_bemade_fsm_equipment_type__id
|
||||||
#: model:ir.model.fields,field_description:bemade_fsm.field_bemade_fsm_visit__id
|
#: model:ir.model.fields,field_description:bemade_fsm.field_bemade_fsm_visit__id
|
||||||
|
#: model:ir.model.fields,field_description:bemade_fsm.field_project_task_from_template_wizard__id
|
||||||
#: model:ir.model.fields,field_description:bemade_fsm.field_project_task_template__id
|
#: model:ir.model.fields,field_description:bemade_fsm.field_project_task_template__id
|
||||||
msgid "ID"
|
msgid "ID"
|
||||||
msgstr ""
|
msgstr "Identifiant"
|
||||||
|
|
||||||
#. module: bemade_fsm
|
#. module: bemade_fsm
|
||||||
#: model:ir.model.fields,field_description:bemade_fsm.field_bemade_fsm_equipment__activity_exception_icon
|
#: model:ir.model.fields,field_description:bemade_fsm.field_bemade_fsm_equipment__activity_exception_icon
|
||||||
|
|
@ -456,6 +481,8 @@ msgid ""
|
||||||
"Indicates whether a line or all the lines in a section have been entirely "
|
"Indicates whether a line or all the lines in a section have been entirely "
|
||||||
"delivered and invoiced."
|
"delivered and invoiced."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
"Indique si une ligne ou toutes les lignes dans une section ont été "
|
||||||
|
"entièrement livrées et facturées."
|
||||||
|
|
||||||
#. module: bemade_fsm
|
#. module: bemade_fsm
|
||||||
#: model:ir.model.fields,help:bemade_fsm.field_bemade_fsm_visit__is_completed
|
#: model:ir.model.fields,help:bemade_fsm.field_bemade_fsm_visit__is_completed
|
||||||
|
|
@ -487,6 +514,12 @@ msgstr ""
|
||||||
msgid "Invoiced"
|
msgid "Invoiced"
|
||||||
msgstr "Facturé"
|
msgstr "Facturé"
|
||||||
|
|
||||||
|
#. module: bemade_fsm
|
||||||
|
#: model:ir.model.fields,field_description:bemade_fsm.field_sale_order__is_fsm
|
||||||
|
#: model:ir.model.fields,field_description:bemade_fsm.field_sale_order_line__is_fsm
|
||||||
|
msgid "Is FSM"
|
||||||
|
msgstr "Est FSM"
|
||||||
|
|
||||||
#. module: bemade_fsm
|
#. module: bemade_fsm
|
||||||
#: model:ir.model.fields,field_description:bemade_fsm.field_sale_order_line__is_field_service
|
#: model:ir.model.fields,field_description:bemade_fsm.field_sale_order_line__is_field_service
|
||||||
msgid "Is Field Service"
|
msgid "Is Field Service"
|
||||||
|
|
@ -501,7 +534,7 @@ msgstr "Est un abonné"
|
||||||
#: model:ir.model.fields,field_description:bemade_fsm.field_res_partner__is_site_contact
|
#: model:ir.model.fields,field_description:bemade_fsm.field_res_partner__is_site_contact
|
||||||
#: model:ir.model.fields,field_description:bemade_fsm.field_res_users__is_site_contact
|
#: model:ir.model.fields,field_description:bemade_fsm.field_res_users__is_site_contact
|
||||||
msgid "Is a site contact"
|
msgid "Is a site contact"
|
||||||
msgstr ""
|
msgstr "Est un contact sur site"
|
||||||
|
|
||||||
#. module: bemade_fsm
|
#. module: bemade_fsm
|
||||||
#: model:ir.model.fields,field_description:bemade_fsm.field_bemade_fsm_visit__label
|
#: model:ir.model.fields,field_description:bemade_fsm.field_bemade_fsm_visit__label
|
||||||
|
|
@ -513,6 +546,7 @@ msgstr "Nom"
|
||||||
#: model:ir.model.fields,field_description:bemade_fsm.field_bemade_fsm_equipment_tag____last_update
|
#: model:ir.model.fields,field_description:bemade_fsm.field_bemade_fsm_equipment_tag____last_update
|
||||||
#: model:ir.model.fields,field_description:bemade_fsm.field_bemade_fsm_equipment_type____last_update
|
#: model:ir.model.fields,field_description:bemade_fsm.field_bemade_fsm_equipment_type____last_update
|
||||||
#: model:ir.model.fields,field_description:bemade_fsm.field_bemade_fsm_visit____last_update
|
#: model:ir.model.fields,field_description:bemade_fsm.field_bemade_fsm_visit____last_update
|
||||||
|
#: model:ir.model.fields,field_description:bemade_fsm.field_project_task_from_template_wizard____last_update
|
||||||
#: model:ir.model.fields,field_description:bemade_fsm.field_project_task_template____last_update
|
#: model:ir.model.fields,field_description:bemade_fsm.field_project_task_template____last_update
|
||||||
msgid "Last Modified on"
|
msgid "Last Modified on"
|
||||||
msgstr "Dernière modification"
|
msgstr "Dernière modification"
|
||||||
|
|
@ -522,6 +556,7 @@ msgstr "Dernière modification"
|
||||||
#: model:ir.model.fields,field_description:bemade_fsm.field_bemade_fsm_equipment_tag__write_uid
|
#: model:ir.model.fields,field_description:bemade_fsm.field_bemade_fsm_equipment_tag__write_uid
|
||||||
#: model:ir.model.fields,field_description:bemade_fsm.field_bemade_fsm_equipment_type__write_uid
|
#: model:ir.model.fields,field_description:bemade_fsm.field_bemade_fsm_equipment_type__write_uid
|
||||||
#: model:ir.model.fields,field_description:bemade_fsm.field_bemade_fsm_visit__write_uid
|
#: model:ir.model.fields,field_description:bemade_fsm.field_bemade_fsm_visit__write_uid
|
||||||
|
#: model:ir.model.fields,field_description:bemade_fsm.field_project_task_from_template_wizard__write_uid
|
||||||
#: model:ir.model.fields,field_description:bemade_fsm.field_project_task_template__write_uid
|
#: model:ir.model.fields,field_description:bemade_fsm.field_project_task_template__write_uid
|
||||||
msgid "Last Updated by"
|
msgid "Last Updated by"
|
||||||
msgstr "Modifié par"
|
msgstr "Modifié par"
|
||||||
|
|
@ -531,6 +566,7 @@ msgstr "Modifié par"
|
||||||
#: model:ir.model.fields,field_description:bemade_fsm.field_bemade_fsm_equipment_tag__write_date
|
#: model:ir.model.fields,field_description:bemade_fsm.field_bemade_fsm_equipment_tag__write_date
|
||||||
#: model:ir.model.fields,field_description:bemade_fsm.field_bemade_fsm_equipment_type__write_date
|
#: model:ir.model.fields,field_description:bemade_fsm.field_bemade_fsm_equipment_type__write_date
|
||||||
#: model:ir.model.fields,field_description:bemade_fsm.field_bemade_fsm_visit__write_date
|
#: model:ir.model.fields,field_description:bemade_fsm.field_bemade_fsm_visit__write_date
|
||||||
|
#: model:ir.model.fields,field_description:bemade_fsm.field_project_task_from_template_wizard__write_date
|
||||||
#: model:ir.model.fields,field_description:bemade_fsm.field_project_task_template__write_date
|
#: model:ir.model.fields,field_description:bemade_fsm.field_project_task_template__write_date
|
||||||
msgid "Last Updated on"
|
msgid "Last Updated on"
|
||||||
msgstr "Dernière mise à jour"
|
msgstr "Dernière mise à jour"
|
||||||
|
|
@ -550,6 +586,11 @@ msgstr "Pièce jointe principale"
|
||||||
msgid "Mark as Delivered"
|
msgid "Mark as Delivered"
|
||||||
msgstr "Marquer comme livré"
|
msgstr "Marquer comme livré"
|
||||||
|
|
||||||
|
#. module: bemade_fsm
|
||||||
|
#: model_terms:ir.ui.view,arch_db:bemade_fsm.workorder_page_materials_table
|
||||||
|
msgid "Materials"
|
||||||
|
msgstr "Matériaux"
|
||||||
|
|
||||||
#. module: bemade_fsm
|
#. module: bemade_fsm
|
||||||
#: model:ir.model.fields,field_description:bemade_fsm.field_bemade_fsm_equipment__message_has_error
|
#: model:ir.model.fields,field_description:bemade_fsm.field_bemade_fsm_equipment__message_has_error
|
||||||
msgid "Message Delivery error"
|
msgid "Message Delivery error"
|
||||||
|
|
@ -560,6 +601,15 @@ msgstr "Erreur de livraison de message"
|
||||||
msgid "Messages"
|
msgid "Messages"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: bemade_fsm
|
||||||
|
#: code:addons/bemade_fsm/models/task.py:0
|
||||||
|
#, python-format
|
||||||
|
msgid ""
|
||||||
|
"Modifying the planned start or end time on a task is not permitted when that"
|
||||||
|
" task already has planning records associated to it. Please modify or delete"
|
||||||
|
" the planning records instead."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#. module: bemade_fsm
|
#. module: bemade_fsm
|
||||||
#: model:ir.model.fields,field_description:bemade_fsm.field_bemade_fsm_equipment__my_activity_date_deadline
|
#: model:ir.model.fields,field_description:bemade_fsm.field_bemade_fsm_equipment__my_activity_date_deadline
|
||||||
msgid "My Activity Deadline"
|
msgid "My Activity Deadline"
|
||||||
|
|
@ -573,9 +623,14 @@ msgid "Name"
|
||||||
msgstr "Nom"
|
msgstr "Nom"
|
||||||
|
|
||||||
#. module: bemade_fsm
|
#. module: bemade_fsm
|
||||||
#: model:ir.model.fields,field_description:bemade_fsm.field_bemade_fsm_equipment__activity_calendar_event_id
|
#: model:ir.model.fields,field_description:bemade_fsm.field_project_task_from_template_wizard__new_task_title
|
||||||
msgid "Next Activity Calendar Event"
|
msgid "New Task Title"
|
||||||
msgstr "Évènement au calendrier de la prochaine activité"
|
msgstr "Nouveau titre de tâche"
|
||||||
|
|
||||||
|
#. module: bemade_fsm
|
||||||
|
#: model:ir.actions.act_window,name:bemade_fsm.action_new_task_from_template
|
||||||
|
msgid "New Task from Template"
|
||||||
|
msgstr "Nouvelle tâche à partir d'un gabarit"
|
||||||
|
|
||||||
#. module: bemade_fsm
|
#. module: bemade_fsm
|
||||||
#: model:ir.model.fields,field_description:bemade_fsm.field_bemade_fsm_equipment__activity_date_deadline
|
#: model:ir.model.fields,field_description:bemade_fsm.field_bemade_fsm_equipment__activity_date_deadline
|
||||||
|
|
@ -618,7 +673,8 @@ msgid "Number of unread messages"
|
||||||
msgstr "Nombre de messages non-lus"
|
msgstr "Nombre de messages non-lus"
|
||||||
|
|
||||||
#. module: bemade_fsm
|
#. module: bemade_fsm
|
||||||
#: model_terms:ir.ui.view,arch_db:bemade_fsm.workorder_page_sale_order_table
|
#: model_terms:ir.ui.view,arch_db:bemade_fsm.workorder_page_materials_table
|
||||||
|
#: model_terms:ir.ui.view,arch_db:bemade_fsm.workorder_page_sale_order_table_with_pricing_and_labour
|
||||||
msgid "Ordered"
|
msgid "Ordered"
|
||||||
msgstr "Commandé"
|
msgstr "Commandé"
|
||||||
|
|
||||||
|
|
@ -636,6 +692,7 @@ msgid "P&ID Tag"
|
||||||
msgstr "Tag P&ID"
|
msgstr "Tag P&ID"
|
||||||
|
|
||||||
#. module: bemade_fsm
|
#. module: bemade_fsm
|
||||||
|
#: model_terms:ir.ui.view,arch_db:bemade_fsm.project_task_view_search_fsm_inherit
|
||||||
#: model_terms:ir.ui.view,arch_db:bemade_fsm.task_template_search_view
|
#: model_terms:ir.ui.view,arch_db:bemade_fsm.task_template_search_view
|
||||||
msgid "Parent Task"
|
msgid "Parent Task"
|
||||||
msgstr "Tâche parent"
|
msgstr "Tâche parent"
|
||||||
|
|
@ -661,10 +718,25 @@ msgstr "Notes sur l'emplacement physique"
|
||||||
msgid "Plan as field service"
|
msgid "Plan as field service"
|
||||||
msgstr "Planifier comme service terrain"
|
msgstr "Planifier comme service terrain"
|
||||||
|
|
||||||
|
#. module: bemade_fsm
|
||||||
|
#: model_terms:ir.ui.view,arch_db:bemade_fsm.workorder_page_info_block
|
||||||
|
msgid "Planned end:"
|
||||||
|
msgstr "Fin planifiée :"
|
||||||
|
|
||||||
|
#. module: bemade_fsm
|
||||||
|
#: model_terms:ir.ui.view,arch_db:bemade_fsm.workorder_page_info_block
|
||||||
|
msgid "Purchase Order:"
|
||||||
|
msgstr "Commande :"
|
||||||
|
|
||||||
|
#. module: bemade_fsm
|
||||||
|
#: model_terms:ir.ui.view,arch_db:bemade_fsm.workorder_page_info_block
|
||||||
|
msgid "Planned start:"
|
||||||
|
msgstr "Début planifié :"
|
||||||
|
|
||||||
#. module: bemade_fsm
|
#. module: bemade_fsm
|
||||||
#: model:ir.model,name:bemade_fsm.model_product_template
|
#: model:ir.model,name:bemade_fsm.model_product_template
|
||||||
msgid "Product Template"
|
msgid "Product Template"
|
||||||
msgstr "Modèle de produit"
|
msgstr "Modèle d'article"
|
||||||
|
|
||||||
#. module: bemade_fsm
|
#. module: bemade_fsm
|
||||||
#: model:ir.model.fields,help:bemade_fsm.field_product_product__is_field_service
|
#: model:ir.model.fields,help:bemade_fsm.field_product_product__is_field_service
|
||||||
|
|
@ -677,10 +749,21 @@ msgstr ""
|
||||||
" considéré lors de la planification."
|
" considéré lors de la planification."
|
||||||
|
|
||||||
#. module: bemade_fsm
|
#. module: bemade_fsm
|
||||||
|
#: model:ir.model.fields,field_description:bemade_fsm.field_project_task_from_template_wizard__project_id
|
||||||
#: model_terms:ir.ui.view,arch_db:bemade_fsm.task_template_search_view
|
#: model_terms:ir.ui.view,arch_db:bemade_fsm.task_template_search_view
|
||||||
msgid "Project"
|
msgid "Project"
|
||||||
msgstr "Projet"
|
msgstr "Projet"
|
||||||
|
|
||||||
|
#. module: bemade_fsm
|
||||||
|
#: model:ir.model.fields,field_description:bemade_fsm.field_project_task__propagate_assignment
|
||||||
|
msgid "Propagate Assignment"
|
||||||
|
msgstr "Propager l'assignation"
|
||||||
|
|
||||||
|
#. module: bemade_fsm
|
||||||
|
#: model:ir.model.fields,help:bemade_fsm.field_project_task__propagate_assignment
|
||||||
|
msgid "Propagate assignment of this task to all subtasks."
|
||||||
|
msgstr "Propager l'assignation de cette tâche à toutes les sous-tâches."
|
||||||
|
|
||||||
#. module: bemade_fsm
|
#. module: bemade_fsm
|
||||||
#: model:project.task.type,legend_done:bemade_fsm.planning_project_stage_exception
|
#: model:project.task.type,legend_done:bemade_fsm.planning_project_stage_exception
|
||||||
#: model:project.task.type,legend_done:bemade_fsm.planning_project_stage_waiting_parts
|
#: model:project.task.type,legend_done:bemade_fsm.planning_project_stage_waiting_parts
|
||||||
|
|
@ -717,12 +800,17 @@ msgstr "Section de commande de vente"
|
||||||
#: model:ir.model,name:bemade_fsm.model_sale_order
|
#: model:ir.model,name:bemade_fsm.model_sale_order
|
||||||
#: model:ir.model.fields,field_description:bemade_fsm.field_bemade_fsm_visit__sale_order_id
|
#: model:ir.model.fields,field_description:bemade_fsm.field_bemade_fsm_visit__sale_order_id
|
||||||
msgid "Sales Order"
|
msgid "Sales Order"
|
||||||
msgstr "Commande de ventes"
|
msgstr "Bon de commande"
|
||||||
|
|
||||||
#. module: bemade_fsm
|
#. module: bemade_fsm
|
||||||
#: model:ir.model,name:bemade_fsm.model_sale_order_line
|
#: model:ir.model,name:bemade_fsm.model_sale_order_line
|
||||||
msgid "Sales Order Line"
|
msgid "Sales Order Line"
|
||||||
msgstr "Ligne de commandes de vente"
|
msgstr "Ligne de bons de commande"
|
||||||
|
|
||||||
|
#. module: bemade_fsm
|
||||||
|
#: model:ir.model.fields,field_description:bemade_fsm.field_sale_order_line__section_line_ids
|
||||||
|
msgid "Section Line"
|
||||||
|
msgstr "Section"
|
||||||
|
|
||||||
#. module: bemade_fsm
|
#. module: bemade_fsm
|
||||||
#: model:ir.model.fields,field_description:bemade_fsm.field_project_task_template__sequence
|
#: model:ir.model.fields,field_description:bemade_fsm.field_project_task_template__sequence
|
||||||
|
|
@ -730,7 +818,9 @@ msgid "Sequence"
|
||||||
msgstr "Séquence"
|
msgstr "Séquence"
|
||||||
|
|
||||||
#. module: bemade_fsm
|
#. module: bemade_fsm
|
||||||
|
#: code:addons/bemade_fsm/models/sale_order.py:0
|
||||||
#: model:ir.model.fields,field_description:bemade_fsm.field_bemade_fsm_visit__task_id
|
#: model:ir.model.fields,field_description:bemade_fsm.field_bemade_fsm_visit__task_id
|
||||||
|
#, python-format
|
||||||
msgid "Service Visit"
|
msgid "Service Visit"
|
||||||
msgstr "Visite de service"
|
msgstr "Visite de service"
|
||||||
|
|
||||||
|
|
@ -759,13 +849,14 @@ msgstr "Contacts sur site :"
|
||||||
|
|
||||||
#. module: bemade_fsm
|
#. module: bemade_fsm
|
||||||
#: model:ir.model.fields,field_description:bemade_fsm.field_res_partner__equipment_ids
|
#: model:ir.model.fields,field_description:bemade_fsm.field_res_partner__equipment_ids
|
||||||
|
#: model:ir.model.fields,field_description:bemade_fsm.field_res_users__equipment_ids
|
||||||
msgid "Site Equipment"
|
msgid "Site Equipment"
|
||||||
msgstr ""
|
msgstr "Équipement sur site"
|
||||||
|
|
||||||
#. module: bemade_fsm
|
#. module: bemade_fsm
|
||||||
#: model:ir.model.fields,field_description:bemade_fsm.field_project_task__planned_date_begin
|
#: model:ir.model.fields,field_description:bemade_fsm.field_project_task__planned_date_begin
|
||||||
msgid "Start Date"
|
msgid "Start Date"
|
||||||
msgstr ""
|
msgstr "Date de début"
|
||||||
|
|
||||||
#. module: bemade_fsm
|
#. module: bemade_fsm
|
||||||
#: model_terms:ir.ui.view,arch_db:bemade_fsm.workorder_page_tasks_table
|
#: model_terms:ir.ui.view,arch_db:bemade_fsm.workorder_page_tasks_table
|
||||||
|
|
@ -816,6 +907,7 @@ msgstr "Tâche"
|
||||||
#: model:ir.actions.act_window,name:bemade_fsm.task_template_act_window
|
#: model:ir.actions.act_window,name:bemade_fsm.task_template_act_window
|
||||||
#: model:ir.model.fields,field_description:bemade_fsm.field_product_product__task_template_id
|
#: model:ir.model.fields,field_description:bemade_fsm.field_product_product__task_template_id
|
||||||
#: model:ir.model.fields,field_description:bemade_fsm.field_product_template__task_template_id
|
#: model:ir.model.fields,field_description:bemade_fsm.field_product_template__task_template_id
|
||||||
|
#: model:ir.model.fields,field_description:bemade_fsm.field_project_task_from_template_wizard__task_template_id
|
||||||
#: model_terms:ir.ui.view,arch_db:bemade_fsm.task_template_form_view
|
#: model_terms:ir.ui.view,arch_db:bemade_fsm.task_template_form_view
|
||||||
#: model_terms:ir.ui.view,arch_db:bemade_fsm.task_template_search_view
|
#: model_terms:ir.ui.view,arch_db:bemade_fsm.task_template_search_view
|
||||||
#: model_terms:ir.ui.view,arch_db:bemade_fsm.task_template_tree_view
|
#: model_terms:ir.ui.view,arch_db:bemade_fsm.task_template_tree_view
|
||||||
|
|
@ -835,14 +927,10 @@ msgstr "Nom de tâche"
|
||||||
|
|
||||||
#. module: bemade_fsm
|
#. module: bemade_fsm
|
||||||
#: model_terms:ir.ui.view,arch_db:bemade_fsm.workorder_page_tasks_table
|
#: model_terms:ir.ui.view,arch_db:bemade_fsm.workorder_page_tasks_table
|
||||||
|
#: model_terms:ir.ui.view,arch_db:bemade_fsm.workorder_page_timesheet_entries
|
||||||
msgid "Technician"
|
msgid "Technician"
|
||||||
msgstr "Technicien·ne"
|
msgstr "Technicien·ne"
|
||||||
|
|
||||||
#. module: bemade_fsm
|
|
||||||
#: model_terms:ir.ui.view,arch_db:bemade_fsm.workorder_page_info_block
|
|
||||||
msgid "Technicians:"
|
|
||||||
msgstr "Technicien·ne·s:"
|
|
||||||
|
|
||||||
#. module: bemade_fsm
|
#. module: bemade_fsm
|
||||||
#: model:ir.model,name:bemade_fsm.model_project_task_template
|
#: model:ir.model,name:bemade_fsm.model_project_task_template
|
||||||
msgid "Template for new project tasks"
|
msgid "Template for new project tasks"
|
||||||
|
|
@ -855,6 +943,11 @@ msgstr ""
|
||||||
"L'équipement à désservir par défaut pour les nouveaux articles de commande "
|
"L'équipement à désservir par défaut pour les nouveaux articles de commande "
|
||||||
"de ventes."
|
"de ventes."
|
||||||
|
|
||||||
|
#. module: bemade_fsm
|
||||||
|
#: model:ir.model.fields,help:bemade_fsm.field_project_task_from_template_wizard__project_id
|
||||||
|
msgid "The project the new task should be created in."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#. module: bemade_fsm
|
#. module: bemade_fsm
|
||||||
#: model:ir.model.fields,help:bemade_fsm.field_bemade_fsm_visit__so_section_id
|
#: model:ir.model.fields,help:bemade_fsm.field_bemade_fsm_visit__so_section_id
|
||||||
msgid ""
|
msgid ""
|
||||||
|
|
@ -864,6 +957,20 @@ msgstr ""
|
||||||
"La section sur la commande de vente représentant la main d'oeuvre et les "
|
"La section sur la commande de vente représentant la main d'oeuvre et les "
|
||||||
"pièces pour cette visite"
|
"pièces pour cette visite"
|
||||||
|
|
||||||
|
#. module: bemade_fsm
|
||||||
|
#: model:ir.model.fields,help:bemade_fsm.field_project_task_from_template_wizard__task_template_id
|
||||||
|
msgid "The template to use when creating the new task."
|
||||||
|
msgstr "Le gabarit à utiliser en créant la nouvelle tâche."
|
||||||
|
|
||||||
|
#. module: bemade_fsm
|
||||||
|
#: model:ir.model.fields,help:bemade_fsm.field_project_task_from_template_wizard__new_task_title
|
||||||
|
msgid ""
|
||||||
|
"The title (name) for the newly created task. If left blank, the name of the "
|
||||||
|
"template will be used."
|
||||||
|
msgstr ""
|
||||||
|
"Le titre (nom) de la nouvelle tâche. Si laissé vide, le nom du gabarit sera "
|
||||||
|
"utilisé."
|
||||||
|
|
||||||
#. module: bemade_fsm
|
#. module: bemade_fsm
|
||||||
#: model_terms:ir.actions.act_window,help:bemade_fsm.task_template_act_window
|
#: model_terms:ir.actions.act_window,help:bemade_fsm.task_template_act_window
|
||||||
msgid "There are no task templates, click above to create one."
|
msgid "There are no task templates, click above to create one."
|
||||||
|
|
@ -871,7 +978,22 @@ msgstr ""
|
||||||
"Il n'existe aucun gabarit de tâche, cliquez ci-dessus pour en créer une."
|
"Il n'existe aucun gabarit de tâche, cliquez ci-dessus pour en créer une."
|
||||||
|
|
||||||
#. module: bemade_fsm
|
#. module: bemade_fsm
|
||||||
#: model_terms:ir.ui.view,arch_db:bemade_fsm.workorder_page_sale_order_table
|
#: code:addons/bemade_fsm/models/sale_order_line.py:0
|
||||||
|
#, python-format
|
||||||
|
msgid ""
|
||||||
|
"This task has been created from: <a href=# data-oe-model=sale.order data-oe-"
|
||||||
|
"id=%d>%s</a> (%s)"
|
||||||
|
msgstr ""
|
||||||
|
"Cette tâche a été créée à partir de : <a href=# data-oe-model=sale.order data-oe-"
|
||||||
|
"id=%d>%s</a> (%s)"
|
||||||
|
|
||||||
|
#. module: bemade_fsm
|
||||||
|
#: model_terms:ir.ui.view,arch_db:bemade_fsm.workorder_page_timesheet_entries
|
||||||
|
msgid "Time"
|
||||||
|
msgstr "Temps"
|
||||||
|
|
||||||
|
#. module: bemade_fsm
|
||||||
|
#: model_terms:ir.ui.view,arch_db:bemade_fsm.workorder_page_sale_order_table_with_pricing_and_labour
|
||||||
msgid "Time & Material"
|
msgid "Time & Material"
|
||||||
msgstr "Temps & matériaux"
|
msgstr "Temps & matériaux"
|
||||||
|
|
||||||
|
|
@ -886,7 +1008,7 @@ msgid "Type of the exception activity on record."
|
||||||
msgstr "Type d'activité d'exception sur l'enregistrement."
|
msgstr "Type d'activité d'exception sur l'enregistrement."
|
||||||
|
|
||||||
#. module: bemade_fsm
|
#. module: bemade_fsm
|
||||||
#: model_terms:ir.ui.view,arch_db:bemade_fsm.workorder_page_sale_order_table
|
#: model_terms:ir.ui.view,arch_db:bemade_fsm.workorder_page_sale_order_table_with_pricing_and_labour
|
||||||
msgid "Unit Price"
|
msgid "Unit Price"
|
||||||
msgstr "Prix unitaire"
|
msgstr "Prix unitaire"
|
||||||
|
|
||||||
|
|
@ -906,13 +1028,19 @@ msgid "View Task"
|
||||||
msgstr "Voir tâche"
|
msgstr "Voir tâche"
|
||||||
|
|
||||||
#. module: bemade_fsm
|
#. module: bemade_fsm
|
||||||
|
#: code:addons/bemade_fsm/models/sale_order_line.py:0
|
||||||
#: model:ir.model.fields,field_description:bemade_fsm.field_project_task__visit_id
|
#: model:ir.model.fields,field_description:bemade_fsm.field_project_task__visit_id
|
||||||
#: model:ir.model.fields,field_description:bemade_fsm.field_sale_order__visit_ids
|
#: model:ir.model.fields,field_description:bemade_fsm.field_sale_order__visit_ids
|
||||||
#: model:ir.model.fields,field_description:bemade_fsm.field_sale_order_line__visit_id
|
#: model:ir.model.fields,field_description:bemade_fsm.field_sale_order_line__visit_id
|
||||||
#: model:ir.model.fields,field_description:bemade_fsm.field_sale_order_line__visit_ids
|
#, python-format
|
||||||
msgid "Visit"
|
msgid "Visit"
|
||||||
msgstr "Visite"
|
msgstr "Visite"
|
||||||
|
|
||||||
|
#. module: bemade_fsm
|
||||||
|
#: model:ir.model.fields,field_description:bemade_fsm.field_sale_order_line__visit_ids
|
||||||
|
msgid "Visits"
|
||||||
|
msgstr "Visites"
|
||||||
|
|
||||||
#. module: bemade_fsm
|
#. module: bemade_fsm
|
||||||
#: model:project.task.type,name:bemade_fsm.planning_project_stage_waiting_parts
|
#: model:project.task.type,name:bemade_fsm.planning_project_stage_waiting_parts
|
||||||
msgid "Waiting on Parts"
|
msgid "Waiting on Parts"
|
||||||
|
|
@ -928,24 +1056,23 @@ msgstr "Messages du site web"
|
||||||
msgid "Website communication history"
|
msgid "Website communication history"
|
||||||
msgstr "Historique de communication du site web"
|
msgstr "Historique de communication du site web"
|
||||||
|
|
||||||
|
#. module: bemade_fsm
|
||||||
|
#: model_terms:ir.ui.view,arch_db:bemade_fsm.workorder_page_timesheet_entries
|
||||||
|
msgid "Work Completed"
|
||||||
|
msgstr "Travaux complétés"
|
||||||
|
|
||||||
#. module: bemade_fsm
|
#. module: bemade_fsm
|
||||||
#: model:project.task.type,name:bemade_fsm.planning_project_stage_work_completed
|
#: model:project.task.type,name:bemade_fsm.planning_project_stage_work_completed
|
||||||
msgid "Work Executed"
|
msgid "Work Executed"
|
||||||
msgstr "Travaux exécutés"
|
msgstr "Travaux exécutés"
|
||||||
|
|
||||||
#. module: bemade_fsm
|
|
||||||
#: model_terms:ir.ui.view,arch_db:bemade_fsm.workorder_page_info_block
|
|
||||||
msgid "Work Order"
|
|
||||||
msgstr "Bon de travail"
|
|
||||||
|
|
||||||
#. module: bemade_fsm
|
#. module: bemade_fsm
|
||||||
#: model_terms:ir.ui.view,arch_db:bemade_fsm.workorder_page_info_block
|
#: model_terms:ir.ui.view,arch_db:bemade_fsm.workorder_page_info_block
|
||||||
msgid ""
|
msgid ""
|
||||||
"Work Order\n"
|
"Work Order\n"
|
||||||
" Contacts:"
|
" Contacts:"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Bon de travail\n"
|
"Contacts recevant le bon de travail :"
|
||||||
" Contacts :"
|
|
||||||
|
|
||||||
#. module: bemade_fsm
|
#. module: bemade_fsm
|
||||||
#: model:ir.model.fields,field_description:bemade_fsm.field_project_task__work_order_contacts
|
#: model:ir.model.fields,field_description:bemade_fsm.field_project_task__work_order_contacts
|
||||||
|
|
|
||||||
|
|
@ -72,4 +72,5 @@ class Equipment(models.Model):
|
||||||
def _compute_complete_name(self):
|
def _compute_complete_name(self):
|
||||||
for rec in self:
|
for rec in self:
|
||||||
tag_part = "[%s] " % rec.pid_tag if rec.pid_tag else ""
|
tag_part = "[%s] " % rec.pid_tag if rec.pid_tag else ""
|
||||||
rec.complete_name = tag_part + rec.name
|
name = rec.name or ""
|
||||||
|
rec.complete_name = tag_part + name
|
||||||
|
|
|
||||||
|
|
@ -76,6 +76,12 @@ class Task(models.Model):
|
||||||
|
|
||||||
work_order_number = fields.Char(readonly=True)
|
work_order_number = fields.Char(readonly=True)
|
||||||
|
|
||||||
|
propagate_assignment = fields.Boolean(
|
||||||
|
string='Propagate Assignment',
|
||||||
|
help='Propagate assignment of this task to all subtasks.',
|
||||||
|
default=False,
|
||||||
|
)
|
||||||
|
|
||||||
@api.model_create_multi
|
@api.model_create_multi
|
||||||
def create(self, vals):
|
def create(self, vals):
|
||||||
res = super().create(vals)
|
res = super().create(vals)
|
||||||
|
|
@ -88,10 +94,23 @@ class Task(models.Model):
|
||||||
pattern = re.compile(r"\d+$")
|
pattern = re.compile(r"\d+$")
|
||||||
matches = map(lambda n: pattern.search(n), prev_seqs)
|
matches = map(lambda n: pattern.search(n), prev_seqs)
|
||||||
seq += max(map(lambda n: int(n.group(1)) if n else 0), matches)
|
seq += max(map(lambda n: int(n.group(1)) if n else 0), matches)
|
||||||
rec.work_order_number = rec.sale_order_id.name.replace('SO', 'WO', 1) \
|
rec.work_order_number = rec.sale_order_id.name.replace('SO', 'SVR', 1) \
|
||||||
+ f"-{seq}"
|
+ f"-{seq}"
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
def write(self, vals):
|
||||||
|
super().write(vals)
|
||||||
|
if not self: # End recursion on empty RecordSet
|
||||||
|
return
|
||||||
|
if 'propagate_assignment' in vals:
|
||||||
|
# When a user sets propagate assignment, it should propagate that setting all the way down the chain
|
||||||
|
self.child_ids.write({'propagate_assignment': vals['propagate_assignment']})
|
||||||
|
if 'user_ids' in vals:
|
||||||
|
to_propagate = self.filtered(lambda task: task.propagate_assignment)
|
||||||
|
# Here we use child_ids instead of _get_all_subtasks() so as to allow for setting propagate_assignment
|
||||||
|
# to false on a child task.
|
||||||
|
to_propagate.child_ids.write({'user_ids': vals['user_ids']})
|
||||||
|
|
||||||
@api.depends('sale_order_id')
|
@api.depends('sale_order_id')
|
||||||
def _compute_relevant_order_lines(self):
|
def _compute_relevant_order_lines(self):
|
||||||
for rec in self:
|
for rec in self:
|
||||||
|
|
@ -234,6 +253,11 @@ class Task(models.Model):
|
||||||
|
|
||||||
task.write(values)
|
task.write(values)
|
||||||
|
|
||||||
|
def _get_full_hierarchy(self):
|
||||||
|
if self.child_ids:
|
||||||
|
return self | self.child_ids._get_full_hierarchy()
|
||||||
|
return self
|
||||||
|
|
||||||
def synchronize_name_fsm(self):
|
def synchronize_name_fsm(self):
|
||||||
""" Applies naming to the entire task tree for tasks that are part of this
|
""" Applies naming to the entire task tree for tasks that are part of this
|
||||||
recordset. Root tasks are named:
|
recordset. Root tasks are named:
|
||||||
|
|
|
||||||
|
|
@ -81,3 +81,38 @@ class TaskTemplate(models.Model):
|
||||||
for rec in self:
|
for rec in self:
|
||||||
new_equipment_ids = [eq.id for eq in rec.equipment_ids if eq.partner_location_id == rec.customer]
|
new_equipment_ids = [eq.id for eq in rec.equipment_ids if eq.partner_location_id == rec.customer]
|
||||||
rec.write({'equipment_ids': [Command.set(new_equipment_ids)]})
|
rec.write({'equipment_ids': [Command.set(new_equipment_ids)]})
|
||||||
|
|
||||||
|
def _prepare_new_task_values_from_self(self, project, name=False, parent_id=False):
|
||||||
|
vals = {
|
||||||
|
'project_id': project.id,
|
||||||
|
'name': name or self.name,
|
||||||
|
'description': self.description,
|
||||||
|
'parent_id': parent_id,
|
||||||
|
'user_ids': self.assignees.ids,
|
||||||
|
'tag_ids': self.tags.ids,
|
||||||
|
'planned_hours': self.planned_hours,
|
||||||
|
'sequence': self.sequence,
|
||||||
|
'equipment_ids': [Command.set(self.equipment_ids.ids)] if self.equipment_ids else False,
|
||||||
|
'partner_id': project.partner_id and project.partner_id.id,
|
||||||
|
'company_id': self.company_id.id,
|
||||||
|
}
|
||||||
|
return vals
|
||||||
|
|
||||||
|
def create_task_from_self(self, project, name=False, parent_id=False):
|
||||||
|
""" Create a project.task from this template and return it. Can be called on a RecordSet of multiple templates.
|
||||||
|
|
||||||
|
:param project: project.project record the task should be added to
|
||||||
|
:param name: name for the new task (defaults to template name)
|
||||||
|
:param parent_id: parent task for the new task (none by default)
|
||||||
|
:return: project.task record created from this template
|
||||||
|
"""
|
||||||
|
tasks = self.env['project.task']
|
||||||
|
for rec in self:
|
||||||
|
vals = rec._prepare_new_task_values_from_self(project, name, parent_id)
|
||||||
|
task = rec.env['project.task'].create(vals)
|
||||||
|
rec.subtasks.create_task_from_self(project, name=False, parent_id=task.id)
|
||||||
|
tasks |= task
|
||||||
|
return tasks
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,61 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" ?>
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
<odoo>
|
<odoo>
|
||||||
<data>
|
<data>
|
||||||
<template id="workorder_page_sale_order_table">
|
<template id="workorder_page_materials_table">
|
||||||
|
<t t-set="order_lines"
|
||||||
|
t-value="doc.relevant_order_lines.filtered(lambda l:
|
||||||
|
l.product_id.type != 'service' and not l.is_downpayment and not l.visit_id)"/>
|
||||||
|
<t t-if="order_lines">
|
||||||
|
<t t-set="visit_lines" t-value="order_lines.mapped('visit_id')"/>
|
||||||
|
<t t-set="section_lines"
|
||||||
|
t-value="order_lines.filtered(lambda l: l.display_type == 'line_section')"/>
|
||||||
|
</t>
|
||||||
|
<div t-if="order_lines.filtered(lambda l: not l.display_type)" style="page-break-inside: avoid;">
|
||||||
|
<h2 t-if="order_lines">Materials</h2>
|
||||||
|
<div t-if="order_lines" class="table-responsive-sm">
|
||||||
|
<table class="table table-sm o_main_table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="text-left">Description</th>
|
||||||
|
<th class="text-right">Ordered</th>
|
||||||
|
<th class="text-right">Delivered</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody class="sale_tbody">
|
||||||
|
<t t-foreach="order_lines" t-as="line">
|
||||||
|
<t t-set="is_task" t-value="line == doc.sale_line_id"/>
|
||||||
|
<tr t-att-class="'bg-200 font-weight-bold o_line_section' if line.display_type == 'line_section' else 'font-italic o_line_note' if line.display_type == 'line_note' else ''">
|
||||||
|
<t t-if="not line.display_type">
|
||||||
|
<td><span t-field="line.name"/></td>
|
||||||
|
<td class="text-right">
|
||||||
|
<span t-field="line.product_uom_qty"/>
|
||||||
|
<span t-field="line.product_uom"
|
||||||
|
groups="uom.group_uom"/>
|
||||||
|
</td>
|
||||||
|
<td class="text-right">
|
||||||
|
<span t-field="line.qty_delivered"/>
|
||||||
|
<span t-field="line.product_uom"
|
||||||
|
groups="uom.group_uom"/>
|
||||||
|
</td>
|
||||||
|
</t>
|
||||||
|
<t t-if="line.display_type == 'line_section'">
|
||||||
|
<td colspan="99">
|
||||||
|
<span t-field="line.name"/>
|
||||||
|
</td>
|
||||||
|
</t>
|
||||||
|
<t t-if="line.display_type == 'line_note'">
|
||||||
|
<td colspan="99">
|
||||||
|
<span t-field="line.name"/>
|
||||||
|
</td>
|
||||||
|
</t>
|
||||||
|
</tr>
|
||||||
|
</t>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template id="workorder_page_sale_order_table_with_pricing_and_labour">
|
||||||
<t t-set="order_lines" t-value="doc.relevant_order_lines"/>
|
<t t-set="order_lines" t-value="doc.relevant_order_lines"/>
|
||||||
<t t-if="order_lines">
|
<t t-if="order_lines">
|
||||||
<t t-set="visit_lines" t-value="order_lines.mapped('visit_id')"/>
|
<t t-set="visit_lines" t-value="order_lines.mapped('visit_id')"/>
|
||||||
|
|
@ -164,7 +218,6 @@
|
||||||
</template>
|
</template>
|
||||||
<template id="workorder_page_tasks_table">
|
<template id="workorder_page_tasks_table">
|
||||||
<t t-set="interventions" t-value="doc.child_ids"/>
|
<t t-set="interventions" t-value="doc.child_ids"/>
|
||||||
<t t-set="timesheets" t-value="doc.timesheet_ids"/>
|
|
||||||
<t t-foreach="interventions" t-as="intervention">
|
<t t-foreach="interventions" t-as="intervention">
|
||||||
<div style="page-break-inside: avoid;">
|
<div style="page-break-inside: avoid;">
|
||||||
<h2 t-out="str(intervention_index + 1) + '. ' + intervention.name"/>
|
<h2 t-out="str(intervention_index + 1) + '. ' + intervention.name"/>
|
||||||
|
|
@ -182,9 +235,8 @@
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th width="7%">Status</th>
|
<th width="7%">Status</th>
|
||||||
<th width="30%">Task</th>
|
<th width="39%">Task</th>
|
||||||
<th width="18%">Technician</th>
|
<th width="54%">Comments</th>
|
||||||
<th width="45%">Comments</th>
|
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
|
|
@ -194,11 +246,6 @@
|
||||||
<td>
|
<td>
|
||||||
<span t-out="task.name"/>
|
<span t-out="task.name"/>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
|
||||||
<t t-foreach="task.user_ids" t-as="user">
|
|
||||||
<span t-out="user.name + (', ' if not user_last else '')"/>
|
|
||||||
</t>
|
|
||||||
</td>
|
|
||||||
<td t-out="task.description or ''"/>
|
<td t-out="task.description or ''"/>
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="mt-0 pt-0">
|
<tr class="mt-0 pt-0">
|
||||||
|
|
@ -207,7 +254,6 @@
|
||||||
<t t-call="bemade_fsm.subtask_list"/>
|
<t t-call="bemade_fsm.subtask_list"/>
|
||||||
</td>
|
</td>
|
||||||
<td/>
|
<td/>
|
||||||
<td/>
|
|
||||||
</tr>
|
</tr>
|
||||||
</t>
|
</t>
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|
@ -218,20 +264,16 @@
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
<template id="workorder_page_info_block">
|
<template id="workorder_page_info_block">
|
||||||
<h1>Work Order <span t-out="doc.work_order_number"></span></h1>
|
<h1>
|
||||||
<div class="row">
|
<span name="work_order_number" t-out="doc.name"/>
|
||||||
<div t-attf-class="{{'col-6' if report_type == 'pdf' else 'col-md-6 col-12'}}">
|
</h1>
|
||||||
<div t-if="doc.user_ids"><h6>Technicians: </h6></div>
|
<h4 t-if="doc.sale_order_id and doc.sale_order_id.client_order_ref">
|
||||||
<t t-foreach="doc.user_ids" t-as="user">
|
Purchase Order: <span
|
||||||
<div class="mb-3">
|
name="po_number"
|
||||||
<div t-esc="user" t-options='{
|
t-out="doc.sale_order_id.client_order_ref"/>
|
||||||
"widget": "contact",
|
</h4>
|
||||||
"fields": ["name", "email"]
|
<hr/>
|
||||||
}'/>
|
<div class="row" name="address_and_time">
|
||||||
</div>
|
|
||||||
</t>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div t-attf-class="{{('col-6' if report_type == 'pdf' else 'col-md-6 col-12') + ' mb-3'}}">
|
<div t-attf-class="{{('col-6' if report_type == 'pdf' else 'col-md-6 col-12') + ' mb-3'}}">
|
||||||
<t t-if="doc.partner_id">
|
<t t-if="doc.partner_id">
|
||||||
<div><h6>Customer: </h6></div>
|
<div><h6>Customer: </h6></div>
|
||||||
|
|
@ -241,8 +283,19 @@
|
||||||
}'/>
|
}'/>
|
||||||
</t>
|
</t>
|
||||||
</div>
|
</div>
|
||||||
|
<div t-attf-class="{{('col-6' if report_type == 'pdf' else 'col-md-6 col-12') + ' mb-3'}}"
|
||||||
|
t-if="doc.planned_date_begin or doc.planned_date_end">
|
||||||
|
<div t-if="doc.planned_date_begin"><h6>Planned start: </h6></div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<div t-out="doc.planned_date_begin.strftime('%Y-%m-%d %H:%M')"/>
|
||||||
|
</div>
|
||||||
|
<div t-if="doc.planned_date_end"><h6>Planned end: </h6></div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<div t-out="doc.planned_date_end.strftime('%Y-%m-%d %H:%M')"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row" name="site_and_work_order_contacts">
|
||||||
<div t-attf-class="{{'col-6' if report_type == 'pdf' else 'col-md-6 col-12'}}">
|
<div t-attf-class="{{'col-6' if report_type == 'pdf' else 'col-md-6 col-12'}}">
|
||||||
<div t-if="doc.site_contacts"><h6>Site Contacts: </h6></div>
|
<div t-if="doc.site_contacts"><h6>Site Contacts: </h6></div>
|
||||||
<t t-foreach="doc.site_contacts" t-as="contact">
|
<t t-foreach="doc.site_contacts" t-as="contact">
|
||||||
|
|
@ -267,13 +320,20 @@
|
||||||
</t>
|
</t>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<hr/>
|
||||||
|
<div t-if="not is_html_empty(doc.description)" class="row" name="visit_description">
|
||||||
|
<div class="col-12">
|
||||||
|
<span t-out="doc.description" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr t-if="not is_html_empty(doc.description)"/>
|
||||||
</template>
|
</template>
|
||||||
<template id="workorder_equipment_summary">
|
<template id="workorder_equipment_summary">
|
||||||
<div t-if="doc.equipment_ids and len(doc.child_ids) > 1"
|
<div t-if="doc.equipment_ids and len(doc.child_ids) > 1"
|
||||||
style="page-break-inside: avoid;">
|
style="page-break-inside: avoid;">
|
||||||
<h3>Equipment Serviced</h3>
|
<h3>Equipment Serviced</h3>
|
||||||
<div class="table-responsive-sm">
|
<div class="table-responsive-sm">
|
||||||
<table class="table table-sm o_main_table">
|
<table class="table table-sm o_main_table overflow-hidden">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Tag</th>
|
<th>Tag</th>
|
||||||
|
|
@ -305,6 +365,47 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
<template id="workorder_page_timesheet_entries">
|
||||||
|
<t t-set="timesheets" t-value="doc.timesheet_ids"/>
|
||||||
|
<div t-if="timesheets" style="page-break-inside: avoid;">
|
||||||
|
<h1>Time</h1>
|
||||||
|
<div class="table-responsive-sm">
|
||||||
|
<table class="table table-sm o_main_table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th width="14%">Date</th>
|
||||||
|
<th width="30%">Technician</th>
|
||||||
|
<th width="49%">Work Completed</th>
|
||||||
|
<th width="7%" class="text-right">Time</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr t-foreach="timesheets" t-as="timesheet" class="p-1 m-1">
|
||||||
|
<td>
|
||||||
|
<span t-out="timesheet.date.strftime('%Y-%m-%d')"/>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span t-field="timesheet.employee_id.name"/>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span t-field="timesheet.name"/>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span t-esc="timesheet.unit_amount"
|
||||||
|
t-options="{
|
||||||
|
'widget': 'duration',
|
||||||
|
'digital': True,
|
||||||
|
'unit': 'hour',
|
||||||
|
'round': 'minute'
|
||||||
|
}"
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
<template id="workorder_page_signature_block">
|
<template id="workorder_page_signature_block">
|
||||||
<div t-if="doc.worksheet_signature">
|
<div t-if="doc.worksheet_signature">
|
||||||
<div t-if="report_type == html" class="ribbon" style="
|
<div t-if="report_type == html" class="ribbon" style="
|
||||||
|
|
@ -342,7 +443,8 @@
|
||||||
<template id="work_order_page">
|
<template id="work_order_page">
|
||||||
<div class="page">
|
<div class="page">
|
||||||
<t t-call="bemade_fsm.workorder_page_info_block"/>
|
<t t-call="bemade_fsm.workorder_page_info_block"/>
|
||||||
<t t-call="bemade_fsm.workorder_page_sale_order_table"/>
|
<t t-call="bemade_fsm.workorder_page_timesheet_entries"/>
|
||||||
|
<t t-call="bemade_fsm.workorder_page_materials_table"/>
|
||||||
<t t-call="bemade_fsm.workorder_equipment_summary"/>
|
<t t-call="bemade_fsm.workorder_equipment_summary"/>
|
||||||
<t t-call="bemade_fsm.workorder_page_signature_block"/>
|
<t t-call="bemade_fsm.workorder_page_signature_block"/>
|
||||||
<t t-call="bemade_fsm.workorder_page_tasks_table"/>
|
<t t-call="bemade_fsm.workorder_page_tasks_table"/>
|
||||||
|
|
@ -359,13 +461,14 @@
|
||||||
</t>
|
</t>
|
||||||
</t>
|
</t>
|
||||||
</template>
|
</template>
|
||||||
|
<!-- MD Commented out since this no longer matches the structure of industry_fsm at all -->
|
||||||
<!-- Make the front-end "Sign" page show our new template -->
|
<!-- Make the front-end "Sign" page show our new template -->
|
||||||
<template id="portal_my_worksheet"
|
<!-- <template id="portal_my_worksheet" -->
|
||||||
inherit_id="industry_fsm_report.portal_my_worksheet">
|
<!-- inherit_id="industry_fsm.portal_my_task"> -->
|
||||||
<xpath expr="//div[@t-call='industry_fsm_report.worksheet_custom_page']"
|
<!-- <xpath expr="//div[@t-call='industry_fsm.worksheet_custom_page']" -->
|
||||||
position="replace">
|
<!-- position="replace"> -->
|
||||||
<div t-call="bemade_fsm.work_order_page"/>
|
<!-- <div t-call="bemade_fsm.work_order_page"/> -->
|
||||||
</xpath>
|
<!-- </xpath> -->
|
||||||
</template>
|
<!-- </template> -->
|
||||||
</data>
|
</data>
|
||||||
</odoo>
|
</odoo>
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,16 @@
|
||||||
<?xml version="1.0" encoding="utf-8" ?>
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
<odoo>
|
<odoo>
|
||||||
<data>
|
<data>
|
||||||
<record id="industry_fsm_report.task_custom_report" model="ir.actions.report">
|
<record id="industry_fsm.task_custom_report" model="ir.actions.report">
|
||||||
<field name="name">Work Order Report (PDF)</field>
|
<field name="name">Work Order Report (PDF)</field>
|
||||||
<field name="report_name">bemade_fsm.work_order</field>
|
<field name="report_name">bemade_fsm.work_order</field>
|
||||||
<field name="report_file">bemade_fsm.work_order</field>
|
<field name="report_file">bemade_fsm.work_order</field>
|
||||||
<field name="print_report_name">'%s Work Order %s' % (
|
<field name="print_report_name">'%s %s' % (
|
||||||
time.strftime('%Y-%m-%d'), object.work_order_number)</field>
|
object.planned_date_begin.strftime('%Y-%m-%d') if object.planned_date_begin else time.strftime('%Y-%m-%d'),
|
||||||
|
object.name
|
||||||
|
)
|
||||||
|
</field>
|
||||||
<field name="report_type">qweb-pdf</field>
|
<field name="report_type">qweb-pdf</field>
|
||||||
</record>
|
</record>
|
||||||
</data>
|
</data>
|
||||||
</odoo>
|
</odoo>
|
||||||
|
|
|
||||||
|
|
@ -1,146 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" ?>
|
|
||||||
<odoo>
|
|
||||||
<data>
|
|
||||||
<record id="default_worksheet_template" model="ir.actions.report">
|
|
||||||
<field name="name">Complete Worksheet Report (PDF)</field>
|
|
||||||
<field name="model">project.task</field>
|
|
||||||
<field name="report_type">qweb-pdf</field>
|
|
||||||
<field name="report_name">bemade_fsm.worksheet_complete</field>
|
|
||||||
<field name="print_report_name">
|
|
||||||
'%s Worksheet %s' % (time.strftime('%Y-%m-%d'), object.name)
|
|
||||||
</field>
|
|
||||||
<field name="binding_model_id" ref="model_project_task"/>
|
|
||||||
<field name="binding_type">report</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<template id="worksheet_complete">
|
|
||||||
<t t-call="web.html_container">
|
|
||||||
<t t-foreach="docs" t-as="doc">
|
|
||||||
<t t-call="bemade_fsm.worksheet_complete_page"
|
|
||||||
t-lang="doc.partner_id.lang"/>
|
|
||||||
</t>
|
|
||||||
</t>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template id="worksheet_complete_page">
|
|
||||||
<t t-call="web.external_layout">
|
|
||||||
<t t-set="address" t-if="doc.sale_order_id">
|
|
||||||
<strong class="big-red">Customer:</strong>
|
|
||||||
<div t-field="doc.sale_order_id.partner_id"
|
|
||||||
t-options='{"widget": "contact",
|
|
||||||
"fields": ["address", "name"], "no_marker": True}'/>
|
|
||||||
</t>
|
|
||||||
<t t-set="information_block">
|
|
||||||
<strong>Service Address:</strong>
|
|
||||||
<div t-field="doc.partner_id"
|
|
||||||
t-options='{"widget": "contact",
|
|
||||||
"fields": ["address", "name", "phone"],
|
|
||||||
"no_marker": True, "phone_icons": True}'/>
|
|
||||||
</t>
|
|
||||||
<div class="page">
|
|
||||||
<div class="oe_structure"/>
|
|
||||||
<h2 class="mt-3">
|
|
||||||
<span>Work Order </span>
|
|
||||||
<span t-if="doc.sale_order_id" t-field="doc.sale_order_id.name"/>
|
|
||||||
<span t-else="" t-field="doc.name"/>
|
|
||||||
</h2>
|
|
||||||
<div class="container">
|
|
||||||
<div class="row mb-3 border-dark">
|
|
||||||
<div t-if="doc.sale_order_id.client_order_ref" class="col-4">
|
|
||||||
<span class="font-weight-bolder">Your Reference:</span>
|
|
||||||
<span t-field="doc.sale_order_id.client_order_ref"/>
|
|
||||||
</div>
|
|
||||||
<div t-if="doc.site_contacts" class="col-4">
|
|
||||||
<span class="font-weight-bolder">Site Contacts:</span>
|
|
||||||
<t t-foreach="doc.site_contacts" t-as="contact">
|
|
||||||
<span t-field="contact.name"/>
|
|
||||||
<span t-out="contact"
|
|
||||||
t-options='{"widget": "contact",
|
|
||||||
"fields": ["phone", "mobile", "email"],
|
|
||||||
"no_marker": True, "phone_icons": True}'/>
|
|
||||||
</t>
|
|
||||||
</div>
|
|
||||||
<div t-if="doc.work_order_contacts" class="col-4">
|
|
||||||
<span class="font-weight-bolder">Work order attn:</span>
|
|
||||||
<t t-foreach="doc.work_order_contacts" t-as="contact">
|
|
||||||
<span t-field="contact.name"/>
|
|
||||||
<span t-out="contact"
|
|
||||||
t-options='{"widget": "contact",
|
|
||||||
"fields": ["phone", "mobile", "email"],
|
|
||||||
"no_marker": True, "phone_icons": True}'/>
|
|
||||||
</t>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row mb-3 border-dark">
|
|
||||||
<div class="col-4" t-if="doc.user_ids">
|
|
||||||
<span class="font-weight-bolder">Technician(s)</span>
|
|
||||||
<t t-foreach="doc.user_ids" t-as="technician">
|
|
||||||
<p class="m-0" t-field="technician.name"/>
|
|
||||||
</t>
|
|
||||||
</div>
|
|
||||||
<div class="col-4" t-if="doc.planned_date_begin">
|
|
||||||
<span class="font-weight-bolder">Planned Start</span>
|
|
||||||
<p t-field="doc.planned_date_begin"
|
|
||||||
t-options="{'widget': 'datetime'}"/>
|
|
||||||
</div>
|
|
||||||
<div class="col-4" t-if="doc.planned_date_end">
|
|
||||||
<span class="font-weight-bolder">Planned End</span>
|
|
||||||
<p t-field="doc.planned_date_end"
|
|
||||||
t-options="{'widget': 'datetime'}"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<div class="container">
|
|
||||||
<h3 class="mb32"><strong>Interventions</strong></h3>
|
|
||||||
<t t-foreach="doc.child_ids" t-as="intervention">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-6">
|
|
||||||
<h4><span t-field="intervention.name"/></h4>
|
|
||||||
<span t-if="intervention.description"
|
|
||||||
t-field="intervention.description"/>
|
|
||||||
</div>
|
|
||||||
<div t-if="intervention.equipment_ids"
|
|
||||||
class="col-6">
|
|
||||||
<h4>Equipment: </h4>
|
|
||||||
<t t-foreach="intervention.equipment_ids"
|
|
||||||
t-as="equipment">
|
|
||||||
<span t-field="equipment.complete_name"/>
|
|
||||||
<span t-if="equipment_index < equipment_size -1"
|
|
||||||
t-out="', '"/>
|
|
||||||
</t>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<table class="table table-borderless col-12">
|
|
||||||
<thead>
|
|
||||||
<th>Done</th>
|
|
||||||
<th>Task to do</th>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<t t-foreach="intervention.child_ids"
|
|
||||||
t-as="task">
|
|
||||||
<tr class="wo-task">
|
|
||||||
<td style="width: 5%">
|
|
||||||
<input type="checkbox"
|
|
||||||
t-att-checked="task.is_complete"/>
|
|
||||||
</td>
|
|
||||||
<td style="width: 95%">
|
|
||||||
<p t-field="task.name"/>
|
|
||||||
<p t-field="task.description"/>
|
|
||||||
</td>
|
|
||||||
<!-- TODO: Figure out what to do with the old
|
|
||||||
concept of task comments -->
|
|
||||||
</tr>
|
|
||||||
</t>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</t>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</t>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
</data>
|
|
||||||
</odoo>
|
|
||||||
|
|
@ -5,3 +5,4 @@ access_bemade_fsm_equipment,bemade_fsm_equipment,model_bemade_fsm_equipment,base
|
||||||
access_bemade_fsm_equipment_tag,bemade_fsm_equipment_tag,model_bemade_fsm_equipment_tag,base.group_user,1,1,1,1
|
access_bemade_fsm_equipment_tag,bemade_fsm_equipment_tag,model_bemade_fsm_equipment_tag,base.group_user,1,1,1,1
|
||||||
access_bemade_fsm_equipment_type,access_bemade_fsm_equipment_type,model_bemade_fsm_equipment_type,base.group_user,1,0,0,0
|
access_bemade_fsm_equipment_type,access_bemade_fsm_equipment_type,model_bemade_fsm_equipment_type,base.group_user,1,0,0,0
|
||||||
access_bemade_fsm_visit,access_bemade_fsm_visit,model_bemade_fsm_visit,base.group_user,1,1,1,1
|
access_bemade_fsm_visit,access_bemade_fsm_visit,model_bemade_fsm_visit,base.group_user,1,1,1,1
|
||||||
|
access_bemade_fsm_task_wizard,access_bemade_fsm_task_wizard,model_project_task_from_template_wizard,project.group_project_user,1,1,1,1
|
||||||
|
|
|
||||||
|
37
bemade_fsm/static/src/js/kanban_view.js
Normal file
37
bemade_fsm/static/src/js/kanban_view.js
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
/** @odoo-module **/
|
||||||
|
|
||||||
|
import KanbanController from 'web.KanbanController';
|
||||||
|
import KanbanView from 'web.KanbanView';
|
||||||
|
const viewRegistry = require('web.view_registry')
|
||||||
|
|
||||||
|
const ProjectKanbanController = KanbanController.extend({
|
||||||
|
buttons_template: 'project.KanbanView.buttons',
|
||||||
|
custom_events: _.extend({}, KanbanController.prototype.custom_events, {
|
||||||
|
create_from_template: '_onCreateFromTemplate',
|
||||||
|
}),
|
||||||
|
_onCreateFromTemplate(ev) {
|
||||||
|
ev.stopPropagation();
|
||||||
|
const record = this.model.get(this.handle, {raw: true});
|
||||||
|
let context = record.context;
|
||||||
|
context.active_id = record.project_id;
|
||||||
|
this.do_action({
|
||||||
|
type: 'ir.actions.act_window',
|
||||||
|
res_model: 'project.task.from.template.wizard',
|
||||||
|
views: [[false, 'form']],
|
||||||
|
target: 'new',
|
||||||
|
context: {...record.context, res_model: 'project.task'},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
renderButtons ($node) {
|
||||||
|
this._super.apply(this, arguments);
|
||||||
|
this.$buttons.on('click', 'button.o-kanban-button-new-from-template', this._onCreateFromTemplate.bind(this))
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const ProjectKanbanView = KanbanView.extend({
|
||||||
|
config: _.extend({}, KanbanView.prototype.config, {
|
||||||
|
Controller: ProjectKanbanController,
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
viewRegistry.add('project_kanban', ProjectKanbanView)
|
||||||
36
bemade_fsm/static/src/js/list_view.js
Normal file
36
bemade_fsm/static/src/js/list_view.js
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
/** @odoo-module **/
|
||||||
|
|
||||||
|
import ListController from 'web.ListController';
|
||||||
|
import ListView from 'web.ListView';
|
||||||
|
const viewRegistry = require('web.view_registry')
|
||||||
|
|
||||||
|
const ProjectListController = ListController.extend({
|
||||||
|
buttons_template: 'project.ListView.buttons',
|
||||||
|
custom_events: _.extend({}, ListController.prototype.custom_events, {
|
||||||
|
create_from_template: '_onCreateFromTemplate',
|
||||||
|
}),
|
||||||
|
_onCreateFromTemplate(ev) {
|
||||||
|
ev.stopPropagation();
|
||||||
|
const record = this.model.get(this.handle, {raw: true});
|
||||||
|
this.do_action({
|
||||||
|
type: 'ir.actions.act_window',
|
||||||
|
res_model: 'project.task.from.template.wizard',
|
||||||
|
views: [[false, 'form']],
|
||||||
|
target: 'new',
|
||||||
|
context: {...record.context, res_model: 'project.task'},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
renderButtons ($node) {
|
||||||
|
self = this;
|
||||||
|
this._super.apply(this, arguments);
|
||||||
|
this.$buttons.on('click', 'button.o_list_button_add_from_template', this._onCreateFromTemplate.bind(this))
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const ProjectListView = ListView.extend({
|
||||||
|
config: _.extend({}, ListView.prototype.config, {
|
||||||
|
Controller: ProjectListController,
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
viewRegistry.add('project_list', ProjectListView)
|
||||||
21
bemade_fsm/static/src/xml/project_view_buttons.xml
Normal file
21
bemade_fsm/static/src/xml/project_view_buttons.xml
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<templates xml:space="preserve">
|
||||||
|
<!-- Add a button to create a task from a template to task (project) list and kanban views -->
|
||||||
|
<t t-name='project.KanbanView.buttons' t-inherit="web.KanbanView.buttons" t-inherit-mode="primary">
|
||||||
|
<xpath expr="//button[contains(@t-attf-class, 'o-kanban-button-new')]" position="after">
|
||||||
|
<button title="Create Task from Template" t-if="!noCreate" type="button"
|
||||||
|
t-attf-class="btn {{ btnClass }} o-kanban-button-new-from-template">
|
||||||
|
<t t-esc="create_text && (create_text + ' from Template') || _t('Create from Template')"/>
|
||||||
|
</button>
|
||||||
|
</xpath>
|
||||||
|
</t>
|
||||||
|
<t t-name='project.ListView.buttons' t-inherit="web.ListView.buttons" t-inherit-mode="primary">
|
||||||
|
<xpath expr="//button[hasclass('o_list_button_add')]" position="after">
|
||||||
|
<!-- Create is enabled in the parent template at this point; check is done prior -->
|
||||||
|
<button type="button" class="btn ml-1 btn-primary o_list_button_add_from_template"
|
||||||
|
title="Create Task from Template">
|
||||||
|
Create from Template
|
||||||
|
</button>
|
||||||
|
</xpath>
|
||||||
|
</t>
|
||||||
|
</templates>
|
||||||
|
|
@ -1,104 +0,0 @@
|
||||||
/** @odoo-module **/
|
|
||||||
|
|
||||||
import tour from 'web_tour.tour';
|
|
||||||
|
|
||||||
const TEST_COMPANY = "Test Partner";
|
|
||||||
const TEST_EQPT1 = "Test Equipment 1";
|
|
||||||
const TEST_EQPT2 = "Test Equipment 2";
|
|
||||||
tour.register('equipment_base_tour', {
|
|
||||||
test: true,
|
|
||||||
url: '/web',
|
|
||||||
}, /* Create a new "Test Equipment" and link it to "Test Partner Company" */
|
|
||||||
[tour.stepUtils.showAppsMenuItem(), {
|
|
||||||
content: 'Navigate to the Service menu',
|
|
||||||
trigger: '.o_app[data-menu-xmlid="industry_fsm.fsm_menu_root"]',
|
|
||||||
}, {
|
|
||||||
content: 'Navigate to the Clients submenu',
|
|
||||||
trigger: 'button.dropdown-toggle[data-menu-xmlid="bemade_fsm.menu_service_client"]',
|
|
||||||
}, {
|
|
||||||
content: 'Navigate to the Equipment menu',
|
|
||||||
trigger: '.dropdown-item[data-menu-xmlid="bemade_fsm.menu_service_client_equipment"]',
|
|
||||||
}, {
|
|
||||||
content: 'Click the create button',
|
|
||||||
trigger: '.o_list_button_add',
|
|
||||||
}, {
|
|
||||||
content: 'Add a tag',
|
|
||||||
trigger: 'input[name="pid_tag"]',
|
|
||||||
run: 'text TestPIDTag',
|
|
||||||
}, {
|
|
||||||
content: 'Set the name',
|
|
||||||
trigger: 'input[name="name"]',
|
|
||||||
run: `text ${TEST_EQPT2}`,
|
|
||||||
}, {
|
|
||||||
content: 'Set the partner',
|
|
||||||
trigger: 'div[name="partner_location_id"] div div input',
|
|
||||||
run: `text ${TEST_COMPANY}`,
|
|
||||||
}, {
|
|
||||||
content: 'Click the partner in the dropdown',
|
|
||||||
trigger: `li a.dropdown-item:contains(${TEST_COMPANY})`,
|
|
||||||
}, {
|
|
||||||
content: 'Save equipment',
|
|
||||||
trigger: 'button.o_list_button_save',
|
|
||||||
}, {
|
|
||||||
/* Navigate to the client and make sure that there are two equipments saved (one from the Python test case) */
|
|
||||||
content: 'Navigate to the Clients submenu',
|
|
||||||
trigger: 'button.dropdown-toggle[data-menu-xmlid="bemade_fsm.menu_service_client"]',
|
|
||||||
}, {
|
|
||||||
content: 'Click on the clients submenu',
|
|
||||||
trigger: '.dropdown-item[data-menu-xmlid="bemade_fsm.menu_service_client_clients"]',
|
|
||||||
}, {
|
|
||||||
content: 'Search for the Test Partner Company',
|
|
||||||
trigger: 'input.o_searchview_input',
|
|
||||||
extra_trigger: 'li.breadcrumb-item.active:has(span:contains(Customers))',
|
|
||||||
run: `text ${TEST_COMPANY}`
|
|
||||||
}, {
|
|
||||||
content: 'Validate Search',
|
|
||||||
trigger: '.o_menu_item.o_selection_focus',
|
|
||||||
run: 'click',
|
|
||||||
}, {
|
|
||||||
content: 'Open the test client.',
|
|
||||||
trigger: `div.o_kanban_record:has(span:contains(${TEST_COMPANY}))`,
|
|
||||||
}, {
|
|
||||||
content: 'Click the Field Service tab.',
|
|
||||||
trigger: 'a.nav-link:contains(Field Service)',
|
|
||||||
extra_trigger: `h1 span.o_field_partner_autocomplete[name="name"]:contains(${TEST_COMPANY})`,
|
|
||||||
}, {
|
|
||||||
content: 'Make sure we have a first test equipment',
|
|
||||||
trigger: `td:contains(${TEST_EQPT1})`,
|
|
||||||
run: function () {
|
|
||||||
},
|
|
||||||
}, {
|
|
||||||
content: 'Make sure we have a second test equipment',
|
|
||||||
trigger: `div[name="equipment_ids"]:has(td:contains(${TEST_EQPT2}))`,
|
|
||||||
run: function () {
|
|
||||||
},
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
|
|
||||||
tour.register('equipment_sale_order_tour', {
|
|
||||||
test: true,
|
|
||||||
url: '/web',
|
|
||||||
}, [tour.stepUtils.showAppsMenuItem(), {
|
|
||||||
content: 'Navigate to the Sales menu',
|
|
||||||
trigger: '.o_app[data-menu-xmlid="sale.sale_menu_root"]',
|
|
||||||
}, {
|
|
||||||
content: 'Create an order',
|
|
||||||
trigger: 'button.o_list_button_add',
|
|
||||||
}, {
|
|
||||||
content: 'Select the test partner',
|
|
||||||
trigger: 'div[name="partner_id"] input',
|
|
||||||
run: `text ${TEST_COMPANY}`,
|
|
||||||
}, {
|
|
||||||
content: 'Click the partner in the dropdown',
|
|
||||||
trigger: `li a.dropdown-item:contains(${TEST_COMPANY})`,
|
|
||||||
}, {
|
|
||||||
content: 'Save',
|
|
||||||
trigger: 'button.o_form_button_save',
|
|
||||||
}, {
|
|
||||||
content: 'Navigate to the Field Service tab.',
|
|
||||||
trigger: 'a.nav-link[role="tab"]:contains(Field Service)'
|
|
||||||
}, {
|
|
||||||
content: 'Check that the equipment is listed in the tab.',
|
|
||||||
trigger: `li.ui-menu-item > a:contains(${TEST_EQPT1})`,
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
|
|
@ -1,40 +0,0 @@
|
||||||
/** @odoo-module **/
|
|
||||||
|
|
||||||
import tour from 'web_tour.tour';
|
|
||||||
|
|
||||||
const SO_NAME = "TEST ORDER 2"
|
|
||||||
const PRODUCT_NAME = "Test Product 3"
|
|
||||||
tour.register('sale_order_tour', {
|
|
||||||
test: true,
|
|
||||||
url: '/web',
|
|
||||||
},
|
|
||||||
[tour.stepUtils.showAppsMenuItem(), {
|
|
||||||
content: 'Navigate to the Service menu',
|
|
||||||
trigger: '.o_app[data-menu-xmlid="sale.sale_menu_root"]',
|
|
||||||
}, {
|
|
||||||
content: 'Search for the sales order',
|
|
||||||
trigger: 'input.o_searchview_input',
|
|
||||||
run: `text ${SO_NAME}`,
|
|
||||||
}, {
|
|
||||||
content: 'Validate Search',
|
|
||||||
trigger: '.o_menu_item.o_selection_focus',
|
|
||||||
run: 'click',
|
|
||||||
}, {
|
|
||||||
content: 'Open the test order',
|
|
||||||
trigger: `.o_data_cell[name="client_order_ref"]:contains(${SO_NAME})`,
|
|
||||||
}, {
|
|
||||||
content: 'Click the view tasks button',
|
|
||||||
trigger: 'button[name="action_view_task"]',
|
|
||||||
}, /*{
|
|
||||||
content: 'Click the first task',
|
|
||||||
trigger: `div.o_kanban_record:has(span:contains(${PRODUCT_NAME}))`,
|
|
||||||
},*/ {
|
|
||||||
content: 'Click on the ready to invoice button',
|
|
||||||
trigger: 'button[name="action_fsm_validate"]',
|
|
||||||
extra_trigger: `li.breadcrumb-item.active:has(span:contains(${PRODUCT_NAME}))`
|
|
||||||
}, {
|
|
||||||
content: 'View the SO',
|
|
||||||
trigger: 'button[name="action_view_so"]',
|
|
||||||
// extra_trigger: 'button[title="Current state"]:contains(Done)',
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
|
|
@ -1,75 +0,0 @@
|
||||||
/** @odoo-module **/
|
|
||||||
|
|
||||||
import tour from 'web_tour.tour';
|
|
||||||
const TEST_COMPANY = "Test Partner Company";
|
|
||||||
const TEST_EQPT1 = "Test Equipment 1";
|
|
||||||
const TEST_EQPT2 = "Test Equipment 2";
|
|
||||||
tour.register('task_equipment_tour', {
|
|
||||||
test: true,
|
|
||||||
url: '/web',
|
|
||||||
}, /* Create a new "Test Equipment" and link it to "Test Partner Company" */
|
|
||||||
[tour.stepUtils.showAppsMenuItem(), {
|
|
||||||
content: 'Navigate to the Service menu',
|
|
||||||
trigger: '.o_app[data-menu-xmlid="industry_fsm.fsm_menu_root"]',
|
|
||||||
}, {
|
|
||||||
content: 'Navigate to the Clients submenu',
|
|
||||||
trigger: 'button.dropdown-toggle[data-menu-xmlid="bemade_fsm.menu_service_client"]',
|
|
||||||
}, {
|
|
||||||
content: 'Navigate to the Equipment menu',
|
|
||||||
trigger: '.dropdown-item[data-menu-xmlid="bemade_fsm.menu_service_client_equipment"]',
|
|
||||||
}, {
|
|
||||||
content: 'Click the create button',
|
|
||||||
trigger: '.o_list_button_add',
|
|
||||||
extra_trigger: 'li.breadcrumb-item.active:has(span:contains(Equipment))',
|
|
||||||
}, {
|
|
||||||
content: 'Add a tag',
|
|
||||||
trigger: 'input[name="pid_tag"]',
|
|
||||||
run: 'text TestPIDTag',
|
|
||||||
}, {
|
|
||||||
content: 'Set the name',
|
|
||||||
trigger: 'input[name="name"]',
|
|
||||||
run: `text ${TEST_EQPT2}`,
|
|
||||||
}, {
|
|
||||||
content: 'Set the partner',
|
|
||||||
trigger: 'div[name="partner_location_id"] div div input',
|
|
||||||
run: 'text Test Partner Company',
|
|
||||||
}, {
|
|
||||||
content: 'Click the partner in the dropdown',
|
|
||||||
trigger: `li a.dropdown-item:contains(${TEST_COMPANY})`,
|
|
||||||
}, {
|
|
||||||
content: 'Save equipment',
|
|
||||||
trigger: 'button.o_list_button_save',
|
|
||||||
}, {
|
|
||||||
/* Navigate to the client and make sure that there are two equipments saved (one from the Python test case) */
|
|
||||||
content: 'Navigate to the Clients submenu',
|
|
||||||
trigger: 'button.dropdown-toggle[data-menu-xmlid="bemade_fsm.menu_service_client"]',
|
|
||||||
}, {
|
|
||||||
content: 'Click on the clients submenu',
|
|
||||||
trigger: '.dropdown-item[data-menu-xmlid="bemade_fsm.menu_service_client_clients"]',
|
|
||||||
}, {
|
|
||||||
content: 'Search for the Test Partner Company',
|
|
||||||
trigger: 'input.o_searchview_input',
|
|
||||||
extra_trigger: 'li.breadcrumb-item.active:has(span:contains(Customers))',
|
|
||||||
run: `text ${TEST_COMPANY}`
|
|
||||||
}, {
|
|
||||||
content: 'Validate Search',
|
|
||||||
trigger: '.o_menu_item.o_selection_focus',
|
|
||||||
run: 'click',
|
|
||||||
}, {
|
|
||||||
content: 'Open the test client.',
|
|
||||||
trigger: `div.o_kanban_record:has(span:contains(${TEST_COMPANY}))`,
|
|
||||||
}, {
|
|
||||||
content: 'Click the Field Service tab.',
|
|
||||||
trigger: 'a.nav-link:contains(Field Service)',
|
|
||||||
extra_trigger: `h1 span.o_field_partner_autocomplete[name="name"]:contains(${TEST_COMPANY})`,
|
|
||||||
}, {
|
|
||||||
content: 'Make sure we have a first test equipment',
|
|
||||||
/*trigger: `div[name="equipment_ids"]:has(td:contains(${TEST_EQPT1}))`,*/
|
|
||||||
trigger: `td:contains(${TEST_EQPT1})`,
|
|
||||||
run: function() {},
|
|
||||||
}, {
|
|
||||||
content: 'Make sure we have a second test equipment',
|
|
||||||
trigger: `div[name="equipment_ids"]:has(td:contains(${TEST_EQPT2}))`,
|
|
||||||
run: function() {},
|
|
||||||
}
|
|
||||||
])
|
|
||||||
|
|
@ -1,47 +0,0 @@
|
||||||
/** @odoo-module */
|
|
||||||
|
|
||||||
import tour from 'web_tour.tour';
|
|
||||||
|
|
||||||
tour.register('task_template_tour', {
|
|
||||||
test: true,
|
|
||||||
url: '/web',
|
|
||||||
}, /* Make sure the test task is visible via the Project > Task Templates path */
|
|
||||||
[tour.stepUtils.showAppsMenuItem(), {
|
|
||||||
trigger: '.o_app[data-menu-xmlid="project.menu_main_pm"]',
|
|
||||||
}, {
|
|
||||||
content: 'Open the task templates list view.',
|
|
||||||
trigger: '.o_nav_entry[data-menu-xmlid="bemade_fsm.project_task_template_menu"]',
|
|
||||||
}, {
|
|
||||||
content: 'Open the existing task template.',
|
|
||||||
trigger: '.o_data_cell:contains("Template 1"):parent()',
|
|
||||||
}, {
|
|
||||||
content: 'Confirm that the header contains the task title.',
|
|
||||||
trigger: 'span[name="name"]:contains("Template 1")',
|
|
||||||
}, /* Make sure we can see the task template from the product */
|
|
||||||
tour.stepUtils.toggleHomeMenu(),
|
|
||||||
{
|
|
||||||
content: 'Open the sales menu.',
|
|
||||||
trigger: '.o_app[data-menu-xmlid="sale.sale_menu_root"]',
|
|
||||||
}, {
|
|
||||||
content: 'Open the product dropdown menu.',
|
|
||||||
trigger: 'button[data-menu-xmlid="sale.product_menu_catalog"]',
|
|
||||||
}, {
|
|
||||||
content: 'Click the products menu item.',
|
|
||||||
trigger: 'a.dropdown-item[data-menu-xmlid="sale.menu_product_template_action"]',
|
|
||||||
}, {
|
|
||||||
content: 'Search for the test product.',
|
|
||||||
trigger: 'input.o_searchview_input',
|
|
||||||
extra_trigger: 'li.breadcrumb-item.active:has(span:contains(Products))',
|
|
||||||
run: 'text Test Product 1',
|
|
||||||
}, {
|
|
||||||
trigger: '.o_menu_item.o_selection_focus',
|
|
||||||
content: 'Validate search',
|
|
||||||
run: 'click',
|
|
||||||
}, {
|
|
||||||
content: 'Open the test product.',
|
|
||||||
trigger: 'div.o_kanban_record:has(span:contains(Test Product 1))',
|
|
||||||
}, {
|
|
||||||
content: 'Ensure the product_template_id field is displayed with the "Template 1" mention.',
|
|
||||||
trigger: 'a.o_quick_editable[name="task_template_id"]:first-child:contains("Template 1")',
|
|
||||||
},
|
|
||||||
])
|
|
||||||
|
|
@ -4,3 +4,4 @@ from . import test_sale_order
|
||||||
from . import test_equipment
|
from . import test_equipment
|
||||||
from . import test_fsm_contact_setting
|
from . import test_fsm_contact_setting
|
||||||
from . import test_fsm_visit
|
from . import test_fsm_visit
|
||||||
|
from . import test_task
|
||||||
|
|
|
||||||
|
|
@ -19,3 +19,7 @@ class TestEquipment(BemadeFSMBaseTest):
|
||||||
partner_company.write({'equipment_ids': [Command.set([])]})
|
partner_company.write({'equipment_ids': [Command.set([])]})
|
||||||
with self.assertRaises(MissingError):
|
with self.assertRaises(MissingError):
|
||||||
equipment.name
|
equipment.name
|
||||||
|
|
||||||
|
def test_compute_complete_name_when_name_blank(self):
|
||||||
|
equipment = self._generate_equipment(name=False)
|
||||||
|
complete_name = equipment.complete_name
|
||||||
|
|
|
||||||
|
|
@ -251,7 +251,7 @@ class TestSalesOrder(BemadeFSMBaseTest):
|
||||||
self.assertFalse(visit_task.user_ids)
|
self.assertFalse(visit_task.user_ids)
|
||||||
self.assertFalse(subtask1.user_ids)
|
self.assertFalse(subtask1.user_ids)
|
||||||
self.assertFalse(subtask2.user_ids)
|
self.assertFalse(subtask2.user_ids)
|
||||||
|
|
||||||
def test_long_line_name_overflows_to_task_description(self):
|
def test_long_line_name_overflows_to_task_description(self):
|
||||||
so = self._generate_sale_order()
|
so = self._generate_sale_order()
|
||||||
product = self._generate_product()
|
product = self._generate_product()
|
||||||
|
|
|
||||||
56
bemade_fsm/tests/test_task.py
Normal file
56
bemade_fsm/tests/test_task.py
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
from .test_bemade_fsm_common import BemadeFSMBaseTest
|
||||||
|
from odoo.tests.common import tagged
|
||||||
|
from odoo import Command
|
||||||
|
|
||||||
|
|
||||||
|
@tagged('post_install', '-at_install')
|
||||||
|
class TaskTest(BemadeFSMBaseTest):
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
# Chose to set up all tests the same way since this code was becoming very redundant
|
||||||
|
super().setUpClass()
|
||||||
|
cls.so = cls._generate_sale_order()
|
||||||
|
cls.template = cls._generate_task_template(names=['Parent', 'Child', 'Grandchild'], structure=[2, 1])
|
||||||
|
cls.product = cls._generate_product(task_template=cls.template)
|
||||||
|
cls.sol = cls._generate_sale_order_line(sale_order=cls.so, product=cls.product)
|
||||||
|
cls.user = cls._generate_project_manager_user('Bob', 'Bob')
|
||||||
|
cls.so.action_confirm()
|
||||||
|
cls.task = cls.sol.task_id
|
||||||
|
|
||||||
|
def test_reassigning_assignment_propagating_task_changes_subtasks(self):
|
||||||
|
task = self.task
|
||||||
|
task.propagate_assignment = True
|
||||||
|
|
||||||
|
task.write({
|
||||||
|
'user_ids': [Command.set([self.user.id])],
|
||||||
|
'propagate_assignment': True,
|
||||||
|
})
|
||||||
|
|
||||||
|
self.assertTrue(all([t.user_ids == self.user for t in task | task._get_all_subtasks()]))
|
||||||
|
|
||||||
|
def test_reassigning_task_doesnt_propagate_by_default(self):
|
||||||
|
task = self.task
|
||||||
|
task.write({
|
||||||
|
'user_ids': [Command.set([self.user.id])],
|
||||||
|
'propagate_assignment': True,
|
||||||
|
})
|
||||||
|
|
||||||
|
self.assertFalse(any([t.user_ids for t in task.child_ids.child_ids]))
|
||||||
|
|
||||||
|
def test_unset_propagate_assignment_unsets_for_all_children(self):
|
||||||
|
task = self.task
|
||||||
|
# First, set propagation and assign
|
||||||
|
task.propagate_assignment = True
|
||||||
|
task.write({
|
||||||
|
'user_ids': [Command.set([self.user.id])]
|
||||||
|
})
|
||||||
|
# Then, unset propagation for the children and re-set assignment
|
||||||
|
task.child_ids.write({'propagate_assignment': False})
|
||||||
|
self.assertFalse(any([t.propagate_assignment for t in task._get_all_subtasks()]))
|
||||||
|
# Then, test that assigning the parent only assigns its children, not its grandchildren
|
||||||
|
task.write({
|
||||||
|
'user_ids': [Command.set([])]
|
||||||
|
})
|
||||||
|
self.assertTrue(all([not t.user_ids for t in task | task.child_ids]))
|
||||||
|
self.assertTrue(all([t.user_ids == self.user for t in task.child_ids.child_ids]))
|
||||||
|
|
@ -78,3 +78,17 @@ class TestTaskTemplate(BemadeFSMBaseTest):
|
||||||
self.assertEqual(sol1.task_id.name, "Short Name 1")
|
self.assertEqual(sol1.task_id.name, "Short Name 1")
|
||||||
self.assertEqual(sol2.task_id.name, "Short Name 2")
|
self.assertEqual(sol2.task_id.name, "Short Name 2")
|
||||||
self.assertEqual(sol3.task_id.name, "Task")
|
self.assertEqual(sol3.task_id.name, "Task")
|
||||||
|
|
||||||
|
def test_task_creation_directly_from_template(self):
|
||||||
|
project = self.env.ref("industry_fsm.fsm_project")
|
||||||
|
template = self._generate_task_template(names=['Task', 'Child', 'Grandchild'], structure=[2, 1])
|
||||||
|
|
||||||
|
task = template.create_task_from_self(project, "My new task")
|
||||||
|
|
||||||
|
self.assertEqual(len(task.child_ids), len(template.subtasks))
|
||||||
|
self.assertEqual(len(task.child_ids[0].child_ids), len(template.subtasks[0].subtasks))
|
||||||
|
self.assertEqual(len(task.child_ids[1].child_ids), len(template.subtasks[1].subtasks))
|
||||||
|
self.assertEqual(task.name, "My new task")
|
||||||
|
self.assertEqual(task.child_ids[0].name, template.subtasks[0].name)
|
||||||
|
self.assertTrue(all([t.project_id == project for t in task | task._get_all_subtasks()]))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,24 +8,26 @@
|
||||||
<field name="inherit_id" ref="sale_project.product_template_form_view_invoice_policy_inherit_sale_project"/>
|
<field name="inherit_id" ref="sale_project.product_template_form_view_invoice_policy_inherit_sale_project"/>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<xpath expr="//field[@name='project_id']" position="after">
|
<xpath expr="//field[@name='project_id']" position="after">
|
||||||
<field name="task_template_id"
|
<field name="task_template_id"
|
||||||
attrs="{'invisible': [('service_tracking', 'not in', ('task_global_project', 'task_in_project'))]}"
|
invisible="service_tracking not in ('task_global_project', 'task_in_project')"
|
||||||
domain="[('parent', '=', False)]"/>
|
domain="[('parent', '=', False)]"/>
|
||||||
</xpath>
|
</xpath>
|
||||||
<xpath expr="//field[@name='planning_enabled']" position="after">
|
<xpath expr="//field[@name='planning_enabled']" position="after">
|
||||||
<field name="is_field_service"/>
|
<field name="is_field_service"/>
|
||||||
</xpath>
|
</xpath>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
<record id="product_search_form_view_inherit_bemade_fsm" model="ir.ui.view">
|
|
||||||
<field name="name">bemade_fsm.product_search_form_view_inherit_bemade_fsm</field>
|
<!-- BV: Did comment that one cause can not match inherit_id and don't understand it use -->
|
||||||
<field name="model">product.product</field>
|
<!-- <record id="product_search_form_view_inherit_bemade_fsm" model="ir.ui.view">-->
|
||||||
<field name="inherit_id" ref="industry_fsm_sale.product_search_form_view_inherit_fsm_sale"/>
|
<!-- <field name="name">bemade_fsm.product_search_form_view_inherit_bemade_fsm</field>-->
|
||||||
<field name="arch" type="xml">
|
<!-- <field name="model">product.product</field>-->
|
||||||
<xpath expr="//searchpanel//field[@name='categ_id']" position="attributes">
|
<!-- <field name="inherit_id" ref="industry_fsm_sale.product_search_form_view_inherit_fsm_sale"/>-->
|
||||||
<attribute name="limit">0</attribute>
|
<!-- <field name="arch" type="xml">-->
|
||||||
</xpath>
|
<!-- <xpath expr="//searchpanel//field[@name='categ_id']" position="attributes">-->
|
||||||
</field>
|
<!-- <attribute name="limit">0</attribute>-->
|
||||||
</record>
|
<!-- </xpath>-->
|
||||||
|
<!-- </field>-->
|
||||||
|
<!-- </record>-->
|
||||||
</data>
|
</data>
|
||||||
</odoo>
|
</odoo>
|
||||||
|
|
@ -17,21 +17,22 @@
|
||||||
<page name="field_service" string="Field Service">
|
<page name="field_service" string="Field Service">
|
||||||
<field name="owned_equipment_ids" invisible="True"/>
|
<field name="owned_equipment_ids" invisible="True"/>
|
||||||
<group>
|
<group>
|
||||||
<field name="site_contacts"
|
<field name="site_contacts"
|
||||||
attrs="{'invisible': [('company_type','=','person')]}"
|
invisible="company_type == 'person'"
|
||||||
context="{'tree_view_ref': 'bemade_fsm.fsm_contacts_view_tree'}"/>
|
context="{'tree_view_ref': 'bemade_fsm.fsm_contacts_view_tree'}"/>
|
||||||
<field name="work_order_contacts"
|
<field name="work_order_contacts"
|
||||||
attrs="{'invisible': [('company_type','=','person')]}"
|
invisible="company_type == 'person'"
|
||||||
context="{'tree_view_ref': 'bemade_fsm.fsm_contacts_view_tree'}"/>
|
context="{'tree_view_ref': 'bemade_fsm.fsm_contacts_view_tree'}"/>
|
||||||
<field name="equipment_ids" attrs="{'invisible': [('company_type','=','person')]}"
|
<field name="equipment_ids"
|
||||||
context="{'tree_view_ref': 'bemade_fsm.fsm_equipment_view_tree'}"
|
invisible="company_type == 'person'"
|
||||||
readonly="False"/>
|
context="{'tree_view_ref': 'bemade_fsm.fsm_equipment_view_tree'}"
|
||||||
<field attrs="{'invisible': ['|', ('company_type','=','person'), ('owned_equipment_ids','=',False)]}"
|
readonly="False"/>
|
||||||
context="{'tree_view_ref': 'bemade_fsm.fsm_equipment_view_tree'}"
|
<field name="owned_equipment_ids"
|
||||||
name="owned_equipment_ids"
|
invisible="company_type == 'person' or owned_equipment_ids == False"
|
||||||
readonly="True"/>
|
context="{'tree_view_ref': 'bemade_fsm.fsm_equipment_view_tree'}"
|
||||||
<field name="site_ids"
|
readonly="True"/>
|
||||||
attrs="{'invisible': [('company_type','=','company')]}">
|
<field name="site_ids"
|
||||||
|
invisible="company_type == 'company'">
|
||||||
<tree editable="bottom">
|
<tree editable="bottom">
|
||||||
<field name="name" widget="res_partner_many2one"/>
|
<field name="name" widget="res_partner_many2one"/>
|
||||||
</tree>
|
</tree>
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,7 @@
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<xpath expr="//field[@name='name']/.." position="before">
|
<xpath expr="//field[@name='name']/.." position="before">
|
||||||
<h1 class="d-flex flex-row justify-content-between">
|
<h1 class="d-flex flex-row justify-content-between">
|
||||||
<field name="work_order_number"
|
<field name="work_order_number" invisible="work_order_number == False"/>
|
||||||
attrs="{'invisible': [('work_order_number', '=', False)]}"/>
|
|
||||||
</h1>
|
</h1>
|
||||||
</xpath>
|
</xpath>
|
||||||
<xpath expr="//page[@name='extra_info']" position="after">
|
<xpath expr="//page[@name='extra_info']" position="after">
|
||||||
|
|
@ -46,6 +45,9 @@
|
||||||
<xpath expr="//field[@name='child_ids']/tree//field[@name='name']" position="after">
|
<xpath expr="//field[@name='child_ids']/tree//field[@name='name']" position="after">
|
||||||
<field name="description" string="Description/Comments"/>
|
<field name="description" string="Description/Comments"/>
|
||||||
</xpath>
|
</xpath>
|
||||||
|
<field name="user_ids" position="after">
|
||||||
|
<field name="propagate_assignment"/>
|
||||||
|
</field>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
<!-- Add parent_id = false to domain for My Tasks, All Tasks: To Schedule, All Tasks and To Invoice-->
|
<!-- Add parent_id = false to domain for My Tasks, All Tasks: To Schedule, All Tasks and To Invoice-->
|
||||||
|
|
@ -168,4 +170,4 @@
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
</data>
|
</data>
|
||||||
</odoo>
|
</odoo>
|
||||||
|
|
|
||||||
1
bemade_fsm/wizard/__init__.py
Normal file
1
bemade_fsm/wizard/__init__.py
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
from . import new_task_from_template
|
||||||
51
bemade_fsm/wizard/new_task_from_template.py
Normal file
51
bemade_fsm/wizard/new_task_from_template.py
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
from odoo import models, fields, api, _
|
||||||
|
from odoo.exceptions import UserError
|
||||||
|
|
||||||
|
|
||||||
|
class NewTaskFromTemplateWizard(models.TransientModel):
|
||||||
|
_name = "project.task.from.template.wizard"
|
||||||
|
_description = "Create Task from Template Wizard"
|
||||||
|
|
||||||
|
project_id = fields.Many2one(
|
||||||
|
comodel_name='project.project',
|
||||||
|
string='Project',
|
||||||
|
help='The project the new task should be created in.',
|
||||||
|
required=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
task_template_id = fields.Many2one(
|
||||||
|
comodel_name='project.task.template',
|
||||||
|
string='Task Template',
|
||||||
|
help='The template to use when creating the new task.',
|
||||||
|
required=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
new_task_title = fields.Char(
|
||||||
|
help='The title (name) for the newly created task. If left blank, the name of the template will be used.',
|
||||||
|
)
|
||||||
|
|
||||||
|
def default_get(self, fields_list):
|
||||||
|
res = super().default_get(fields_list)
|
||||||
|
active_id = self.env.context.get('active_id', False)
|
||||||
|
active_model = self.env.context.get('active_model', False)
|
||||||
|
if not active_model:
|
||||||
|
params = self.env.context.get('params', False)
|
||||||
|
active_model = params and params.get('model', False)
|
||||||
|
if active_model == 'project.task.template' and active_id and 'task_template_id' in fields_list:
|
||||||
|
res.update({'task_template_id': active_id})
|
||||||
|
if active_model == 'project.task' and 'project_id' in fields_list:
|
||||||
|
res.update({'project_id': self.env.ref('industry_fsm.fsm_project').id})
|
||||||
|
return res
|
||||||
|
|
||||||
|
def action_create_task_from_template(self):
|
||||||
|
self.ensure_one()
|
||||||
|
task = self.task_template_id.create_task_from_self(self.project_id, self.new_task_title)
|
||||||
|
return {
|
||||||
|
'type': 'ir.actions.act_window',
|
||||||
|
'res_model': 'project.task',
|
||||||
|
'res_id': task.id,
|
||||||
|
'view_mode': 'form',
|
||||||
|
'target': 'current',
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
29
bemade_fsm/wizard/new_task_from_template.xml
Normal file
29
bemade_fsm/wizard/new_task_from_template.xml
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<odoo>
|
||||||
|
<record id="project_task_from_template_wizard_view_form" model="ir.ui.view">
|
||||||
|
<field name="name">project.task.from.template.wizard.view.form</field>
|
||||||
|
<field name="model">project.task.from.template.wizard</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<form>
|
||||||
|
<sheet>
|
||||||
|
<group>
|
||||||
|
<field name="task_template_id"/>
|
||||||
|
<field name="new_task_title"/>
|
||||||
|
<field name="project_id"/>
|
||||||
|
</group>
|
||||||
|
</sheet>
|
||||||
|
<footer>
|
||||||
|
<button class='btn btn-primary' name="action_create_task_from_template" type="object" string="Create Task"/>
|
||||||
|
<button class='btn btn-secondary' special="cancel" string="Cancel"/>
|
||||||
|
</footer>
|
||||||
|
</form>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
<record model="ir.actions.act_window" id="action_new_task_from_template">
|
||||||
|
<field name="name">New Task from Template</field>
|
||||||
|
<field name="res_model">project.task.from.template.wizard</field>
|
||||||
|
<field name="binding_model_id" ref="bemade_fsm.model_project_task_template"/>
|
||||||
|
<field name="view_mode">form</field>
|
||||||
|
<field name="target">new</field>
|
||||||
|
</record>
|
||||||
|
</odoo>
|
||||||
0
bemade_full_formview_from_modal/__init__.py
Normal file
0
bemade_full_formview_from_modal/__init__.py
Normal file
37
bemade_full_formview_from_modal/__manifest__.py
Normal file
37
bemade_full_formview_from_modal/__manifest__.py
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
#
|
||||||
|
# Bemade Inc.
|
||||||
|
#
|
||||||
|
# Copyright (C) 2023-June Bemade Inc. (<https://www.bemade.org>).
|
||||||
|
# Author: Marc Durepos (Contact : mdurepos@durpro.com)
|
||||||
|
#
|
||||||
|
# This program is under the terms of the Odoo Proprietary License v1.0 (OPL-1)
|
||||||
|
# It is forbidden to publish, distribute, sublicense, or sell copies of the Software
|
||||||
|
# or modified copies of the Software.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||||
|
# DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||||
|
# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
# DEALINGS IN THE SOFTWARE.
|
||||||
|
#
|
||||||
|
{
|
||||||
|
'name': 'Full Form from Dialog',
|
||||||
|
'version': '17.0.1.0.0',
|
||||||
|
'summary': 'Allows opening the full form view from the dialog (modal) view.',
|
||||||
|
'description': 'Adds a button to open the full form view when viewing the form view for a record in a dialog.',
|
||||||
|
'category': 'Technical',
|
||||||
|
'author': 'Bemade Inc.',
|
||||||
|
'website': 'http://www.bemade.org',
|
||||||
|
'license': 'OPL-1',
|
||||||
|
'depends': [],
|
||||||
|
'data': [],
|
||||||
|
'assets': {
|
||||||
|
'web.assets_backend': [
|
||||||
|
# 'bemade_full_formview_from_modal/static/src/**/*',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
'installable': True,
|
||||||
|
'auto_install': False
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
/** @odoo-module **/
|
||||||
|
|
||||||
|
import { patch } from "@web/core/utils/patch";
|
||||||
|
import { FormController } from "@web/views/form/form_controller";
|
||||||
|
import { useService } from "@web/core/utils/hooks";
|
||||||
|
|
||||||
|
patch(FormController.prototype, "bemade_full_formview_from_modal.FormController", {
|
||||||
|
setup () {
|
||||||
|
this._super();
|
||||||
|
this.action = useService("action")
|
||||||
|
},
|
||||||
|
onOpenButtonClicked: function () {
|
||||||
|
this.action.doAction({
|
||||||
|
type: "ir.actions.act_window",
|
||||||
|
res_model: this.props.resModel,
|
||||||
|
res_id: this.props.resId,
|
||||||
|
views: [[false, "form"]],
|
||||||
|
target: "current",
|
||||||
|
context: this.props.context,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<templates xml:space="preserve">
|
||||||
|
<t t-inherit="web.FormViewDialog.ToMany.buttons" t-inherit-mode="extension" owl="1">
|
||||||
|
<xpath expr="//button[hasclass('o_form_button_save_new')]" position="after">
|
||||||
|
<button class="btn btn-secondary o_form_button_open" t-on-click="_onOpen">Open</button>
|
||||||
|
</xpath>
|
||||||
|
</t>
|
||||||
|
<t t-inherit="web.FormViewDialog.ToOne.buttons" t-inherit-mode="extension" owl="1">
|
||||||
|
<xpath expr="//button[hasclass('o_form_button_save')]" position="after">
|
||||||
|
<button class="btn btn-secondary o_form_button_open" t-on-click="onOpenButtonClicked">Open</button>
|
||||||
|
</xpath>
|
||||||
|
</t>
|
||||||
|
</templates>
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
<record id="helpdesk_stage_spam" model="helpdesk.stage">
|
<record id="helpdesk_stage_spam" model="helpdesk.stage">
|
||||||
<field name="name">Spam</field>
|
<field name="name">Spam</field>
|
||||||
<field name="sequence">50</field>
|
<field name="sequence">50</field>
|
||||||
<field name="is_close">True</field>
|
<field name="fold">True</field>
|
||||||
</record>
|
</record>
|
||||||
</data>
|
</data>
|
||||||
</odoo>
|
</odoo>
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,8 @@
|
||||||
<field name="inherit_id" ref="helpdesk.helpdesk_ticket_view_form"/>
|
<field name="inherit_id" ref="helpdesk.helpdesk_ticket_view_form"/>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<header position="inside">
|
<header position="inside">
|
||||||
<button name="action_add_blacklist" type="object" string="Add to Blacklist" class="oe_highlight" attrs="{'invisible': [('partner_email', '=', False)]}"/>
|
<field name="partner_email" invisble="1"/>
|
||||||
|
<button name="action_add_blacklist" type="object" string="Add to Blacklist" class="oe_highlight" invisible="partner_email == False"/>
|
||||||
</header>
|
</header>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@
|
||||||
#
|
#
|
||||||
{
|
{
|
||||||
'name': 'Helpdesk One Ticket Per Email',
|
'name': 'Helpdesk One Ticket Per Email',
|
||||||
'version': '15.0.1.0.0',
|
'version': '17.0.1.0.0',
|
||||||
'summary': 'Restrict ticket creation to a single ticket per email received.',
|
'summary': 'Restrict ticket creation to a single ticket per email received.',
|
||||||
'category': 'Helpdesk',
|
'category': 'Helpdesk',
|
||||||
'author': 'Bemade Inc.',
|
'author': 'Bemade Inc.',
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
{
|
{
|
||||||
"name": "Hide Decimal on unit",
|
"name": "Hide Decimal on unit",
|
||||||
"version": "15.0.0.1.1",
|
"version": "17.0.0.1.1",
|
||||||
"category": "Extra Tools",
|
"category": "Extra Tools",
|
||||||
'summary': 'Hide decimal on Qty when there is no decimal',
|
'summary': 'Hide decimal on Qty when there is no decimal',
|
||||||
"description": """
|
"description": """
|
||||||
|
|
@ -14,9 +14,8 @@
|
||||||
'purchase'
|
'purchase'
|
||||||
],
|
],
|
||||||
"data": [
|
"data": [
|
||||||
# BV : FOR MIGRATION
|
'views/sale.xml',
|
||||||
# 'views/sale.xml',
|
'views/purchase.xml'
|
||||||
# 'views/purchase.xml'
|
|
||||||
],
|
],
|
||||||
"auto_install": False,
|
"auto_install": False,
|
||||||
"installable": True,
|
"installable": True,
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<odoo>
|
<odoo>
|
||||||
<template id="report_purchasequotation_document_strip_decimal" inherit_id="purchase.report_purchasequotation_document">
|
<template id="report_purchasequotation_document_strip_decimal" inherit_id="purchase.report_purchasequotation_document">
|
||||||
<xpath expr="//tbody//td[hasclass('text-right')]" position="replace">
|
<xpath expr="//tbody//td[hasclass('text-end')]" position="replace">
|
||||||
<td name="td_quantity" class="text-right">
|
<td name="td_quantity" class="text-end">
|
||||||
<t t-if="int(order_line.product_uom_qty) == order_line.product_uom_qty">
|
<t t-if="int(order_line.product_uom_qty) == order_line.product_uom_qty">
|
||||||
<span t-esc="'%.0f' % order_line.product_uom_qty"/>
|
<span t-esc="'%.0f' % order_line.product_uom_qty"/>
|
||||||
</t>
|
</t>
|
||||||
|
|
@ -15,8 +15,8 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template id="report_purchaseorder_document_strip_decimal" inherit_id="purchase.report_purchaseorder_document">
|
<template id="report_purchaseorder_document_strip_decimal" inherit_id="purchase.report_purchaseorder_document">
|
||||||
<xpath expr="//tbody//td[hasclass('text-right')][1]" position="replace">
|
<xpath expr="//tbody//td[hasclass('text-end')][1]" position="replace">
|
||||||
<td name="td_quantity" class="text-right">
|
<td name="td_quantity" class="text-end">
|
||||||
<t t-if="int(line.product_uom_qty) == line.product_uom_qty">
|
<t t-if="int(line.product_uom_qty) == line.product_uom_qty">
|
||||||
<span t-esc="'%.0f' % line.product_uom_qty"/>
|
<span t-esc="'%.0f' % line.product_uom_qty"/>
|
||||||
</t>
|
</t>
|
||||||
|
|
|
||||||
1
bemade_l10n_ca_payroll/__init__.py
Normal file
1
bemade_l10n_ca_payroll/__init__.py
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
from . import models
|
||||||
37
bemade_l10n_ca_payroll/__manifest__.py
Normal file
37
bemade_l10n_ca_payroll/__manifest__.py
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
#
|
||||||
|
# Bemade Inc.
|
||||||
|
#
|
||||||
|
# Copyright (C) 2023-June Bemade Inc. (<https://www.bemade.org>).
|
||||||
|
# Author: Marc Durepos (Contact : marc@bemade.org)
|
||||||
|
#
|
||||||
|
# This program is under the terms of the Odoo Proprietary License v1.0 (OPL-1)
|
||||||
|
# It is forbidden to publish, distribute, sublicense, or sell copies of the Software
|
||||||
|
# or modified copies of the Software.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||||
|
# DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||||
|
# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
# DEALINGS IN THE SOFTWARE.
|
||||||
|
#
|
||||||
|
{
|
||||||
|
'name': 'Quebec Payroll',
|
||||||
|
'version': '17.0.1.0.0',
|
||||||
|
'summary': 'Computations for Quebec Payslips',
|
||||||
|
'category': 'Human Resources/Payroll',
|
||||||
|
'author': 'Bemade Inc.',
|
||||||
|
'website': 'http://www.bemade.org',
|
||||||
|
'license': 'OPL-1',
|
||||||
|
'depends': [
|
||||||
|
'hr_payroll',
|
||||||
|
'l10n_ca',
|
||||||
|
],
|
||||||
|
'data': [
|
||||||
|
'data/hr_salary_rule_data.xml',
|
||||||
|
],
|
||||||
|
'assets': {},
|
||||||
|
'installable': True,
|
||||||
|
'auto_install': False
|
||||||
|
}
|
||||||
48
bemade_l10n_ca_payroll/data/hr_payslip_input_type_data.xml
Normal file
48
bemade_l10n_ca_payroll/data/hr_payslip_input_type_data.xml
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<odoo>
|
||||||
|
<data>
|
||||||
|
<record id="l10n_ca_input_bonus" model="hr.payslip.input.type">
|
||||||
|
<field name="name">Bonus or other non-period payment</field>
|
||||||
|
<field name="code">BONUS</field>
|
||||||
|
<field name="country_id" ref="base.ca"/>
|
||||||
|
<field name="struct_ids" eval="Command.link(ref('hr_payroll.default_structure'))"/>
|
||||||
|
</record>
|
||||||
|
<record id="l10n_ca_input_fed_f1" model="hr.payslip.input.type">
|
||||||
|
<field name="name">Employee-requested deductions (Federal)</field>
|
||||||
|
<field name="code">FED_F1</field>
|
||||||
|
<field name="country_id" ref="base.ca"/>
|
||||||
|
<field name="struct_ids" eval="Command.link(ref('hr_payroll.default_structure'))"/>
|
||||||
|
</record>
|
||||||
|
<record id="l10n_ca_input_fed_f2" model="hr.payslip.input.type">
|
||||||
|
<field name="name">Court-ordered deductions (Federal)</field>
|
||||||
|
<field name="code">FED_F2</field>
|
||||||
|
<field name="country_id" ref="base.ca"/>
|
||||||
|
<field name="struct_ids" eval="Command.link(ref('hr_payroll.default_structure'))"/>
|
||||||
|
</record>
|
||||||
|
<record id="l10n_ca_input_fed_U1" model="hr.payslip.input.type">
|
||||||
|
<field name="name">Union or association dues for the period (Federal)</field>
|
||||||
|
<field name="code">FED_U1</field>
|
||||||
|
<field name="country_id" ref="base.ca"/>
|
||||||
|
<field name="struct_ids" eval="Command.link(ref('hr_payroll.default_structure'))"/>
|
||||||
|
</record>
|
||||||
|
<record id="l10n_ca_input_fed_HD" model="hr.payslip.input.type">
|
||||||
|
<field name="name">Allowance for residents of specified regions (Federal)</field>
|
||||||
|
<field name="code">FED_HD</field>
|
||||||
|
<field name="country_id" ref="base.ca"/>
|
||||||
|
<field name="struct_ids" eval="Command.link(ref('hr_payroll.default_structure'))"/>
|
||||||
|
</record>
|
||||||
|
<record id="l10n_ca_input_fed_F" model="hr.payslip.input.type">
|
||||||
|
<field name="name">Deduction for retirement plan contributions (Federal)</field>
|
||||||
|
<field name="code">FED_F</field>
|
||||||
|
<field name="country_id" ref="base.ca"/>
|
||||||
|
<field name="struct_ids" eval="Command.link(ref('hr_payroll.default_structure'))"/>
|
||||||
|
</record>
|
||||||
|
<record id="l10n_ca_input_fed_TC" model="hr.payslip.input.type">
|
||||||
|
<field name="name">Total Requested Amount on Federal form TD1</field>
|
||||||
|
<field name="code">FED_TC</field>
|
||||||
|
<field name="country_id" ref="base.ca"/>
|
||||||
|
<field name="struct_ids" eval="Command.link(ref('hr_payroll.default_structure'))"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
146
bemade_l10n_ca_payroll/data/hr_salary_rule_data.xml
Normal file
146
bemade_l10n_ca_payroll/data/hr_salary_rule_data.xml
Normal file
|
|
@ -0,0 +1,146 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<odoo>
|
||||||
|
<data>
|
||||||
|
<record id="l10n_ca_parameter_ARK" model="hr.rule.parameter">
|
||||||
|
<field name="name">A, R, K constants for Federak Tax Calculation</field>
|
||||||
|
<field name="code">ARK</field>
|
||||||
|
</record>
|
||||||
|
<record id="l10n_ca_parameter_value_ARK_2024" model="hr.rule.parameter.value">
|
||||||
|
<field name="date_from">2024-01-01</field>
|
||||||
|
<field name="rule_parameter_id" ref="bemade_l10n_ca_payroll.l10n_ca_parameter_ARK"/>
|
||||||
|
<field name="parameter_value">
|
||||||
|
[
|
||||||
|
(0, 0.15, 0),
|
||||||
|
(55867, 0.2050, 3073),
|
||||||
|
(111733, 0.26, 9218),
|
||||||
|
(173205, 0.29, 14414),
|
||||||
|
(246752, 0.33, 24284),
|
||||||
|
]
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
<record id="l10n_ca_parameter_TC" model="hr.rule.parameter">
|
||||||
|
<field name="name">Base amount for federal form TD1</field>
|
||||||
|
<field name="code">FED_TC</field>
|
||||||
|
</record>
|
||||||
|
<record id="l10n_ca_parameter_value_TC_2024" model="hr.rule.parameter.value">
|
||||||
|
<field name="date_from">2024-01-01</field>
|
||||||
|
<field name="rule_parameter_id" ref="bemade_l10n_ca_payroll.l10n_ca_parameter_TC"/>
|
||||||
|
<field name="parameter_value">15704</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="l10n_ca_fed_tax_on_payslip" model="hr.salary.rule">
|
||||||
|
<field name="name">Income Tax - Federal</field>
|
||||||
|
<field name="category_id" ref="hr_payroll.DED"/>
|
||||||
|
<field name="sequence" eval="205"/>
|
||||||
|
<field name="struct_id" ref="hr_payroll.default_structure"/>
|
||||||
|
<field name="appears_on_payslip" eval="True"/>
|
||||||
|
<field name="appears_on_employee_cost_dashboard" eval="False"/>
|
||||||
|
<field name="appears_on_payroll_report" eval="True"/>
|
||||||
|
<field name="condition_select">none</field>
|
||||||
|
<field name="amount_select">code</field>
|
||||||
|
<field name="amount_python_compute">
|
||||||
|
"""<![CDATA[
|
||||||
|
Source: https://www.canada.ca/fr/agence-revenu/services/formulaires-publications/retenues-paie/t4127-formules-calcul-retenues-paie/t4127-jan/t4127-jan-formules-calcul-informatise-retenues-paie.html#toc31
|
||||||
|
|
||||||
|
Pour le Québec, calcul en 4 étapes (étapes 1, 2, 3, et 6 du guide ci-dessus)
|
||||||
|
Étape 1: Calculer "A" - le revenu imposable
|
||||||
|
Étape 2: Calculer "T3" - l'impôt fédéral de base
|
||||||
|
Étape 3: Calculer "T1" - l'impôt fédéral annuel à payer
|
||||||
|
Étape 4: Calculer "T" - L'impôt à payer pour cette paye
|
||||||
|
|
||||||
|
|
||||||
|
Étape 1
|
||||||
|
|
||||||
|
A = P x (I - F - F2 - F5A -U1) - HD - F1
|
||||||
|
où
|
||||||
|
P = Nombre de périodes de paie dans l'année
|
||||||
|
I = Rémunération brute pour la période de paie, excluant les primes, augmentations salariales rétroactives ou autres paiements non-périodiques
|
||||||
|
F = Retenues pour la période pour un REER, RPA, RPAC ou CR.
|
||||||
|
F2 = saisie ordonnée par la cour (pension alimentaire, etc.)
|
||||||
|
F5A = Déductions des cotisations supplémentaires au RRQ pour la période de paie
|
||||||
|
U1 = Cotisations à un syndicat ou assoc. de fonctionnaires, pour la période de paie
|
||||||
|
HD = Retenue annuelle accordée aux résidents d'une région visée par le règlement selon formulaire TD1
|
||||||
|
F1 = Retenues annuelles (frais de garde d'enfants, pensions alimentaires, demandées par l'employé et autorisés par bureau svcs. fiscaux)
|
||||||
|
PI = gains ouvrant droit à une pension pour la période de paie. Nous assumons ici que c'est égal à la rémunération brute.
|
||||||
|
B = Prime brute, augmentation de salaire rétroactive, ou autres montants non périodiques
|
||||||
|
|
||||||
|
"""
|
||||||
|
pay_periods_map = {
|
||||||
|
'annually': 1,
|
||||||
|
'semi-annually': 2,
|
||||||
|
'quarterly': 4,
|
||||||
|
'bi-monthly': 6,
|
||||||
|
'monthly': 12,
|
||||||
|
'semi-monthly': 24,
|
||||||
|
'bi-weekly': 26,
|
||||||
|
'weekly': 52,
|
||||||
|
'daily': 365,
|
||||||
|
}
|
||||||
|
|
||||||
|
P = payslip.struct_type_id.default_pay_periods_per_year
|
||||||
|
I = categories.get("GROSS") - (inputs['BONUS'].amount if 'BONUS' in inputs else 0)
|
||||||
|
F = inputs['FED_F'].amount if 'FED_F' in inputs else 0
|
||||||
|
F2 = inputs['FED_F2'].amount if 'FED_F2' in inputs else 0
|
||||||
|
C = categories.get("COTISATIONS_RRQ", 0)
|
||||||
|
C2 = categories.get("COTISATIONS_RRQ_2", 0)
|
||||||
|
F5Q = C * (0.01/0.0640) + C2
|
||||||
|
PI = categories.get("GROSS")
|
||||||
|
B = inputs['BONUS'].amount if 'BONUS' in inputs else 0
|
||||||
|
F5A = F5Q * ((PI - B)/PI)
|
||||||
|
U1 = inputs['FED_U1'].amount if 'FED_U1' in inputs else 0
|
||||||
|
HD = inputs['FED_HD'].amount if 'FED_HD' in inputs else 0
|
||||||
|
F1 = inputs['FED_F1'].amount if 'FED_F1' in inputs else 0
|
||||||
|
|
||||||
|
A = P * (I - F - F2 - F5A - U1) - HD - F1
|
||||||
|
|
||||||
|
"""
|
||||||
|
Étape 2 - Calcul de l'impôt fédéral de base (T3)
|
||||||
|
|
||||||
|
T3 = (R x A) - K - K1 - K2Q - K3 - K4
|
||||||
|
où
|
||||||
|
|
||||||
|
R = taux d'imposition fédéral qui s'applique au revenu imposable annuel A
|
||||||
|
"""
|
||||||
|
|
||||||
|
# TODO: Get this into a configuration data structure
|
||||||
|
ARK = payslip._rule_parameter('ARK')
|
||||||
|
R, K = payslip._l10n_ca_compute_fed_tax_constants(A, ARK)
|
||||||
|
TC = inputs['FED_TC'].amount if 'FED_TC' in inputs else payslip._rule_parameter('FED_TC_BASIC')
|
||||||
|
K1 = 0.15 * TC
|
||||||
|
PM = payslip._rule_parameter('RRQ_NO_MOIS_TOTAL')
|
||||||
|
IE = A # Assume that insurables are the gross pay
|
||||||
|
AE = categories.get("EI_CONTR")
|
||||||
|
K2Q = ((0.15 * min(P * C * (0.0540/0.0640), 3217.50) * (PM/12)) + (0.15 * min(P * AE, 834.24) + (0.15 * min(P * IE * 0.00494, 464.36))
|
||||||
|
K3 = inputs['FED_K3'].amount if 'FED_K3' in inputs else 0
|
||||||
|
CCE = payslip._rule_parameter('FED_CCE') # 1 433 for 2024
|
||||||
|
K4 = min(0.5 * A, CCE)
|
||||||
|
|
||||||
|
T3 = (R * A) - K - K1 - K2Q - K3 - K4
|
||||||
|
|
||||||
|
"""
|
||||||
|
Étape 3 - Formule pour calculer l'impôt fédéral à payer (T1)
|
||||||
|
|
||||||
|
T1 = ((T3 - (P x LCF)) - (0.165 * T3)
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
LCF = min(750, 0.15 * (inputs['DED_CAPITAL_PURCH'] if 'DED_CAPITAL_PURCH' in inputs else 0))
|
||||||
|
T1 = (T3 - (P * LCF)) - (0.165 * T3)
|
||||||
|
|
||||||
|
"""
|
||||||
|
Étape 6 - Formule pour calculer une estimation des retenus d'impôt fédéral pour la période de paie (T)
|
||||||
|
|
||||||
|
T = (T1 / P) / L
|
||||||
|
|
||||||
|
L = Retenues d'impôt additionnelles pour la période de paie, demandées par l'employé(e) sur TD1
|
||||||
|
"""
|
||||||
|
|
||||||
|
L = inputs['FED_DEDUCT_REQUEST'].amount if 'FED_DEDUCT_REQUEST' in inputs else 0
|
||||||
|
T = (T1 / P) / L
|
||||||
|
|
||||||
|
result = T
|
||||||
|
]]>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
1
bemade_l10n_ca_payroll/models/__init__.py
Normal file
1
bemade_l10n_ca_payroll/models/__init__.py
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
from . import hr_payroll_structure_type
|
||||||
26
bemade_l10n_ca_payroll/models/hr_payroll_structure_type.py
Normal file
26
bemade_l10n_ca_payroll/models/hr_payroll_structure_type.py
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
from odoo import models, fields, api
|
||||||
|
|
||||||
|
|
||||||
|
class HrPayrollStructureType(models.Model):
|
||||||
|
_inherit = 'hr.payroll.structure.type'
|
||||||
|
|
||||||
|
default_pay_periods_per_year = fields.Integer(
|
||||||
|
compute="_compute_pay_periods_per_year",
|
||||||
|
compute_sudo=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
@api.depends("default_schedule_pay")
|
||||||
|
def _compute_pay_periods_per_year(self):
|
||||||
|
pay_periods_map = {
|
||||||
|
'annually': 1,
|
||||||
|
'semi-annually': 2,
|
||||||
|
'quarterly': 4,
|
||||||
|
'bi-monthly': 6,
|
||||||
|
'monthly': 12,
|
||||||
|
'semi-monthly': 24,
|
||||||
|
'bi-weekly': 26,
|
||||||
|
'weekly': 52,
|
||||||
|
'daily': 365,
|
||||||
|
}
|
||||||
|
for rec in self:
|
||||||
|
rec.default_pay_periods_per_year = pay_periods_map.get(rec.default_schedule_pay, False)
|
||||||
29
bemade_l10n_ca_payroll/models/hr_payslip.py
Normal file
29
bemade_l10n_ca_payroll/models/hr_payslip.py
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
from odoo import models, fields, api
|
||||||
|
|
||||||
|
|
||||||
|
class Payslip(models.Model):
|
||||||
|
_inherit = "hr.payslip"
|
||||||
|
|
||||||
|
def _l18n_ca_compute_fed_tax_constants(self, taxable_income: float, coefficients):
|
||||||
|
"""
|
||||||
|
Take a table of input coefficients in the form
|
||||||
|
[
|
||||||
|
(a, r, k),
|
||||||
|
...
|
||||||
|
] where a, r, and k are the threshold, tax rate and federal constants from government tables,
|
||||||
|
and return the r and k applicable for the given annual taxable income.
|
||||||
|
|
||||||
|
:param taxable_income: annual taxable income
|
||||||
|
:param coefficients: coefficients table to use (get it from rule parameters data, usually)
|
||||||
|
:return: (r, k) values where r is the tax rate and k is the federal constant to use
|
||||||
|
"""
|
||||||
|
R = coefficients[0][1]
|
||||||
|
K = coefficients[0][2]
|
||||||
|
|
||||||
|
# Get the rate and constant by income tier (stop once we reach a tier above the taxable income)
|
||||||
|
for a, r, k in coefficients:
|
||||||
|
if taxable_income < a:
|
||||||
|
return R, K
|
||||||
|
R = r
|
||||||
|
K = k
|
||||||
|
return R, K
|
||||||
|
|
@ -34,8 +34,9 @@
|
||||||
],
|
],
|
||||||
"assets": {
|
"assets": {
|
||||||
"web.assets_backend": [
|
"web.assets_backend": [
|
||||||
"bemade_mailcow_integration/static/src/js/mailcow.js",
|
# BV: Commented out the following lines to avoid errors when installing the module.
|
||||||
"bemade_mailcow_integration/static/src/xml/mailcow_templates.xml",
|
# "bemade_mailcow_integration/static/src/js/mailcow.js",
|
||||||
|
# "bemade_mailcow_integration/static/src/xml/mailcow_templates.xml",
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
'installable': True,
|
'installable': True,
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,9 @@
|
||||||
<record id="view_res_config_settings_form_inherit_mailcow" model="ir.ui.view">
|
<record id="view_res_config_settings_form_inherit_mailcow" model="ir.ui.view">
|
||||||
<field name="name">res.config.settings.form.inherit.mailcow</field>
|
<field name="name">res.config.settings.form.inherit.mailcow</field>
|
||||||
<field name="model">res.config.settings</field>
|
<field name="model">res.config.settings</field>
|
||||||
<field name="inherit_id" ref="base_setup.res_config_settings_view_form"/>
|
<field name="inherit_id" ref="mail.res_config_settings_view_form"/>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<div id="emails" position='after'>
|
<setting id="email_servers_setting" position='after'>
|
||||||
<div id="mailcow">
|
<div id="mailcow">
|
||||||
<h2>Mailcow Settings</h2>
|
<h2>Mailcow Settings</h2>
|
||||||
<div class="row mt16 o_settings_container" name="mailcow_setting">
|
<div class="row mt16 o_settings_container" name="mailcow_setting">
|
||||||
|
|
@ -49,7 +49,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</setting>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
</odoo>
|
</odoo>
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
<field name="inherit_id" ref="base.view_users_form"/>
|
<field name="inherit_id" ref="base.view_users_form"/>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<field name="email" position="before">
|
<field name="email" position="before">
|
||||||
<div id="mailcow_mailcow" attrs="{'invisible': [('id', '!=', False)]}">
|
<div id="mailcow_mailcow" invisible="id != False">
|
||||||
<label for="mailcow_mailbox" string="Create mailbox on Mailcow"/>
|
<label for="mailcow_mailbox" string="Create mailbox on Mailcow"/>
|
||||||
<field name="mailcow_mailbox"/>
|
<field name="mailcow_mailbox"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@
|
||||||
#
|
#
|
||||||
{
|
{
|
||||||
'name': 'Sales Margin on Vendor Price',
|
'name': 'Sales Margin on Vendor Price',
|
||||||
'version': '15.0.0.0.1',
|
'version': '17.0.0.0.1',
|
||||||
'summary': 'Enables calculation of sales margins based on vendor pricelists.',
|
'summary': 'Enables calculation of sales margins based on vendor pricelists.',
|
||||||
'description': """Adds a new method for calculating """,
|
'description': """Adds a new method for calculating """,
|
||||||
'author': 'Bemade Inc.',
|
'author': 'Bemade Inc.',
|
||||||
|
|
|
||||||
|
|
@ -1 +1,2 @@
|
||||||
from . import sale_order
|
from . import sale_order
|
||||||
|
from . import sale_order_line
|
||||||
|
|
@ -21,157 +21,3 @@ class SaleOrder(models.Model):
|
||||||
order.margin_percent_actual = order.amount_untaxed and order.margin_actual / order.amount_untaxed
|
order.margin_percent_actual = order.amount_untaxed and order.margin_actual / order.amount_untaxed
|
||||||
|
|
||||||
|
|
||||||
class SaleOrderLine(models.Model):
|
|
||||||
_inherit = 'sale.order.line'
|
|
||||||
|
|
||||||
purchase_price_vendor = fields.Float(
|
|
||||||
compute='_compute_purchase_price_vendor',
|
|
||||||
string="Vendor Price",
|
|
||||||
groups="base.group_user",
|
|
||||||
digits='Product Price'
|
|
||||||
)
|
|
||||||
|
|
||||||
margin_percent_vendor = fields.Float(
|
|
||||||
string='Margin (%) on Vendor Price',
|
|
||||||
groups='base.group_user',
|
|
||||||
group_operator='avg',
|
|
||||||
compute='_compute_margin_vendor'
|
|
||||||
)
|
|
||||||
|
|
||||||
margin_vendor = fields.Float(
|
|
||||||
string='Margin on Vendor Price',
|
|
||||||
groups='base.group_user',
|
|
||||||
digits='Product Price',
|
|
||||||
compute='_compute_margin_vendor'
|
|
||||||
)
|
|
||||||
|
|
||||||
purchase_price_actual = fields.Float(
|
|
||||||
compute="_compute_actual_margins",
|
|
||||||
digits='Product Price',
|
|
||||||
groups="base.group_user",
|
|
||||||
string="Purchase Price"
|
|
||||||
)
|
|
||||||
|
|
||||||
margin_actual = fields.Float(
|
|
||||||
compute="_compute_actual_margins",
|
|
||||||
digits='Product Price',
|
|
||||||
groups="base.group_user",
|
|
||||||
string="Our Margin"
|
|
||||||
)
|
|
||||||
|
|
||||||
margin_percent_actual = fields.Float(
|
|
||||||
compute="_compute_actual_margins",
|
|
||||||
groups="base.group_user",
|
|
||||||
string="Our Margin (%)"
|
|
||||||
)
|
|
||||||
|
|
||||||
@api.depends(
|
|
||||||
'purchase_price',
|
|
||||||
'purchase_price_vendor',
|
|
||||||
'move_ids.product_id',
|
|
||||||
'move_ids.product_id.qty_available',
|
|
||||||
'move_ids.state',
|
|
||||||
'qty_to_deliver'
|
|
||||||
)
|
|
||||||
def _compute_actual_margins(self):
|
|
||||||
""" We want to use the margin based on average inventory valuation when the
|
|
||||||
sale order line will be completely fulfilled (or has been fulfilled) from stock.
|
|
||||||
For product not yet in stock we want to use the vendor price. We can also have
|
|
||||||
blended calculations (partly on vendor price, partly on stock valuation). This
|
|
||||||
occurs when an order has been or would be partially fulfilled from available
|
|
||||||
stock.
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
non_product_lines = self.filtered(lambda r: not r.product_id)
|
|
||||||
non_product_lines.purchase_price_actual = 0.0
|
|
||||||
non_product_lines.margin_actual = 0.0
|
|
||||||
non_product_lines.margin_percent_actual = 0.0
|
|
||||||
for line in self - non_product_lines:
|
|
||||||
stock_missing = line._determine_missing_stock()
|
|
||||||
if float_is_zero(stock_missing, precision_rounding=line.product_uom.rounding):
|
|
||||||
# everything is coming from stock, use inventory valuation
|
|
||||||
line.purchase_price_actual = line.purchase_price
|
|
||||||
elif float_compare(line.product_uom_qty, stock_missing,
|
|
||||||
precision_rounding=line.product_uom.rounding) == 0:
|
|
||||||
# everything is coming from the vendor, use vendor pricing
|
|
||||||
line.purchase_price_actual = line.purchase_price_vendor
|
|
||||||
else:
|
|
||||||
# we have a mix, use blended pricing
|
|
||||||
qty_from_stock = line.product_uom_qty - stock_missing
|
|
||||||
line.purchase_price_actual = \
|
|
||||||
(stock_missing * line.purchase_price_vendor
|
|
||||||
+ qty_from_stock * line.purchase_price) \
|
|
||||||
/ line.product_uom_qty
|
|
||||||
line.margin_actual = line.price_subtotal - (
|
|
||||||
line.purchase_price_actual * line.product_uom_qty)
|
|
||||||
line.margin_percent_actual = line.price_subtotal and line.margin_actual / line.price_subtotal
|
|
||||||
|
|
||||||
def _determine_missing_stock(self) -> float:
|
|
||||||
""" Compute how much stock is missing to meet an order line's demand. In the
|
|
||||||
case of a quotation line, available stock is checked as if the order were to be
|
|
||||||
placed immediately.
|
|
||||||
|
|
||||||
:return: The quantity missing from available stock to fulfill the line, in
|
|
||||||
the unit of measure matching self.product_uom.
|
|
||||||
"""
|
|
||||||
self.ensure_one()
|
|
||||||
is_order = self.order_id.state in ('sale', 'done')
|
|
||||||
if is_order and self.qty_to_deliver == 0:
|
|
||||||
return 0
|
|
||||||
elif is_order and self.qty_to_deliver > 0:
|
|
||||||
reserved = sum([m.reserved_availability for m in self.move_ids])
|
|
||||||
missing = self.qty_to_deliver - reserved
|
|
||||||
if float_compare(missing, 0.0,
|
|
||||||
precision_rounding=self.product_uom.rounding) == 1:
|
|
||||||
# Not enough reserved, check stock
|
|
||||||
missing = missing - self.product_id.qty_available
|
|
||||||
if float_compare(missing, 0.0,
|
|
||||||
precision_rounding=self.product_uom.rounding) == 1:
|
|
||||||
# Missing some stock to meet demand, return the quantity
|
|
||||||
return missing
|
|
||||||
else:
|
|
||||||
# Enough stock available to meet this line's demand
|
|
||||||
return 0
|
|
||||||
else:
|
|
||||||
# Already have stock reserved
|
|
||||||
return 0
|
|
||||||
else:
|
|
||||||
# This is a quotation, don't bother with stock reservations
|
|
||||||
missing = self.product_uom_qty - self.product_id.qty_available
|
|
||||||
if float_compare(missing, 0.0,
|
|
||||||
precision_rounding=self.product_uom.rounding) == 1:
|
|
||||||
return missing
|
|
||||||
else:
|
|
||||||
return 0
|
|
||||||
|
|
||||||
@api.depends('product_id', 'product_id.seller_ids',
|
|
||||||
'product_id.seller_ids.price')
|
|
||||||
def _compute_purchase_price_vendor(self):
|
|
||||||
for line in self:
|
|
||||||
product = line.product_id
|
|
||||||
suppinfos = product.seller_ids.sorted('sequence')
|
|
||||||
if not suppinfos:
|
|
||||||
line.purchase_price_vendor = 0.0
|
|
||||||
continue
|
|
||||||
suppinfo = suppinfos[0]
|
|
||||||
purch_currency = suppinfo.currency_id
|
|
||||||
to_cur = line.currency_id or line.order_id.currency_id
|
|
||||||
line.purchase_price_vendor = purch_currency._convert(
|
|
||||||
from_amount=suppinfo.price,
|
|
||||||
to_currency=to_cur,
|
|
||||||
company=line.company_id or self.env.company,
|
|
||||||
date=line.order_id.date_order or fields.Date.today(),
|
|
||||||
round=False,
|
|
||||||
) if to_cur and suppinfo.price else suppinfo.price
|
|
||||||
|
|
||||||
@api.depends('purchase_price_vendor')
|
|
||||||
def _compute_margin_vendor(self):
|
|
||||||
for line in self:
|
|
||||||
if not line.price_unit or float_is_zero(line.price_unit):
|
|
||||||
line.margin_vendor = 0
|
|
||||||
line.margin_percent_vendor = 0
|
|
||||||
continue
|
|
||||||
unit_margin = line.price_unit - line.purchase_price_vendor
|
|
||||||
line.margin_percent_vendor = unit_margin / line.price_unit
|
|
||||||
|
|
||||||
line.margin_vendor = unit_margin * line.product_uom_qty
|
|
||||||
|
|
|
||||||
158
bemade_margin_vendor_pricelist/models/sale_order_line.py
Normal file
158
bemade_margin_vendor_pricelist/models/sale_order_line.py
Normal file
|
|
@ -0,0 +1,158 @@
|
||||||
|
from odoo import models, fields, api, _
|
||||||
|
from odoo.tools.float_utils import float_is_zero, float_compare
|
||||||
|
|
||||||
|
|
||||||
|
class SaleOrderLine(models.Model):
|
||||||
|
_inherit = 'sale.order.line'
|
||||||
|
|
||||||
|
purchase_price_vendor = fields.Float(
|
||||||
|
compute='_compute_purchase_price_vendor',
|
||||||
|
string="Vendor Price",
|
||||||
|
groups="base.group_user",
|
||||||
|
digits='Product Price'
|
||||||
|
)
|
||||||
|
|
||||||
|
margin_percent_vendor = fields.Float(
|
||||||
|
string='Margin (%) on Vendor Price',
|
||||||
|
groups='base.group_user',
|
||||||
|
group_operator='avg',
|
||||||
|
compute='_compute_margin_vendor'
|
||||||
|
)
|
||||||
|
|
||||||
|
margin_vendor = fields.Float(
|
||||||
|
string='Margin on Vendor Price',
|
||||||
|
groups='base.group_user',
|
||||||
|
digits='Product Price',
|
||||||
|
compute='_compute_margin_vendor'
|
||||||
|
)
|
||||||
|
|
||||||
|
purchase_price_actual = fields.Float(
|
||||||
|
compute="_compute_actual_margins",
|
||||||
|
digits='Product Price',
|
||||||
|
groups="base.group_user",
|
||||||
|
string="Purchase Price"
|
||||||
|
)
|
||||||
|
|
||||||
|
margin_actual = fields.Float(
|
||||||
|
compute="_compute_actual_margins",
|
||||||
|
digits='Product Price',
|
||||||
|
groups="base.group_user",
|
||||||
|
string="Our Margin"
|
||||||
|
)
|
||||||
|
|
||||||
|
margin_percent_actual = fields.Float(
|
||||||
|
compute="_compute_actual_margins",
|
||||||
|
groups="base.group_user",
|
||||||
|
string="Our Margin (%)"
|
||||||
|
)
|
||||||
|
|
||||||
|
@api.depends(
|
||||||
|
'purchase_price',
|
||||||
|
'purchase_price_vendor',
|
||||||
|
'move_ids.product_id',
|
||||||
|
'move_ids.product_id.qty_available',
|
||||||
|
'move_ids.state',
|
||||||
|
'qty_to_deliver'
|
||||||
|
)
|
||||||
|
def _compute_actual_margins(self):
|
||||||
|
""" We want to use the margin based on average inventory valuation when the
|
||||||
|
sale order line will be completely fulfilled (or has been fulfilled) from stock.
|
||||||
|
For product not yet in stock we want to use the vendor price. We can also have
|
||||||
|
blended calculations (partly on vendor price, partly on stock valuation). This
|
||||||
|
occurs when an order has been or would be partially fulfilled from available
|
||||||
|
stock.
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
non_product_lines = self.filtered(lambda r: not r.product_id)
|
||||||
|
non_product_lines.purchase_price_actual = 0.0
|
||||||
|
non_product_lines.margin_actual = 0.0
|
||||||
|
non_product_lines.margin_percent_actual = 0.0
|
||||||
|
for line in self - non_product_lines:
|
||||||
|
stock_missing = line._determine_missing_stock()
|
||||||
|
if float_is_zero(stock_missing, precision_rounding=line.product_uom.rounding):
|
||||||
|
# everything is coming from stock, use inventory valuation
|
||||||
|
line.purchase_price_actual = line.purchase_price
|
||||||
|
elif float_compare(line.product_uom_qty, stock_missing,
|
||||||
|
precision_rounding=line.product_uom.rounding) == 0:
|
||||||
|
# everything is coming from the vendor, use vendor pricing
|
||||||
|
line.purchase_price_actual = line.purchase_price_vendor
|
||||||
|
else:
|
||||||
|
# we have a mix, use blended pricing
|
||||||
|
qty_from_stock = line.product_uom_qty - stock_missing
|
||||||
|
line.purchase_price_actual = \
|
||||||
|
(stock_missing * line.purchase_price_vendor
|
||||||
|
+ qty_from_stock * line.purchase_price) \
|
||||||
|
/ line.product_uom_qty
|
||||||
|
line.margin_actual = line.price_subtotal - (
|
||||||
|
line.purchase_price_actual * line.product_uom_qty)
|
||||||
|
line.margin_percent_actual = line.price_subtotal and line.margin_actual / line.price_subtotal
|
||||||
|
|
||||||
|
def _determine_missing_stock(self) -> float:
|
||||||
|
""" Compute how much stock is missing to meet an order line's demand. In the
|
||||||
|
case of a quotation line, available stock is checked as if the order were to be
|
||||||
|
placed immediately.
|
||||||
|
|
||||||
|
:return: The quantity missing from available stock to fulfill the line, in
|
||||||
|
the unit of measure matching self.product_uom.
|
||||||
|
"""
|
||||||
|
self.ensure_one()
|
||||||
|
is_order = self.order_id.state in ('sale', 'done')
|
||||||
|
if is_order and self.qty_to_deliver == 0:
|
||||||
|
return 0
|
||||||
|
elif is_order and self.qty_to_deliver > 0:
|
||||||
|
reserved = sum([m.reserved_availability for m in self.move_ids])
|
||||||
|
missing = self.qty_to_deliver - reserved
|
||||||
|
if float_compare(missing, 0.0,
|
||||||
|
precision_rounding=self.product_uom.rounding) == 1:
|
||||||
|
# Not enough reserved, check stock
|
||||||
|
missing = missing - self.product_id.qty_available
|
||||||
|
if float_compare(missing, 0.0,
|
||||||
|
precision_rounding=self.product_uom.rounding) == 1:
|
||||||
|
# Missing some stock to meet demand, return the quantity
|
||||||
|
return missing
|
||||||
|
else:
|
||||||
|
# Enough stock available to meet this line's demand
|
||||||
|
return 0
|
||||||
|
else:
|
||||||
|
# Already have stock reserved
|
||||||
|
return 0
|
||||||
|
else:
|
||||||
|
# This is a quotation, don't bother with stock reservations
|
||||||
|
missing = self.product_uom_qty - self.product_id.qty_available
|
||||||
|
if float_compare(missing, 0.0,
|
||||||
|
precision_rounding=self.product_uom.rounding) == 1:
|
||||||
|
return missing
|
||||||
|
else:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
@api.depends('product_id', 'product_id.seller_ids',
|
||||||
|
'product_id.seller_ids.price')
|
||||||
|
def _compute_purchase_price_vendor(self):
|
||||||
|
for line in self:
|
||||||
|
product = line.product_id
|
||||||
|
suppinfos = product.seller_ids.sorted('sequence')
|
||||||
|
if not suppinfos:
|
||||||
|
line.purchase_price_vendor = 0.0
|
||||||
|
continue
|
||||||
|
suppinfo = suppinfos[0]
|
||||||
|
purch_currency = suppinfo.currency_id
|
||||||
|
to_cur = line.currency_id or line.order_id.currency_id
|
||||||
|
line.purchase_price_vendor = purch_currency._convert(
|
||||||
|
from_amount=suppinfo.price,
|
||||||
|
to_currency=to_cur,
|
||||||
|
company=line.company_id or self.env.company,
|
||||||
|
date=line.order_id.date_order or fields.Date.today(),
|
||||||
|
round=False,
|
||||||
|
) if to_cur and suppinfo.price else suppinfo.price
|
||||||
|
|
||||||
|
@api.depends('purchase_price_vendor')
|
||||||
|
def _compute_margin_vendor(self):
|
||||||
|
for line in self:
|
||||||
|
if not line.price_unit or float_is_zero(line.price_unit):
|
||||||
|
line.margin_vendor = 0
|
||||||
|
line.margin_percent_vendor = 0
|
||||||
|
continue
|
||||||
|
unit_margin = line.price_unit - line.purchase_price_vendor
|
||||||
|
line.margin_percent_vendor = unit_margin / line.price_unit
|
||||||
|
|
||||||
|
line.margin_vendor = unit_margin * line.product_uom_qty
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
'name': 'Module Linker',
|
'name': 'Module Linker',
|
||||||
'version': '15.0.1.0.0',
|
'version': '17.0.1.0.0',
|
||||||
'category': 'Extra Tools',
|
'category': 'Extra Tools',
|
||||||
'summary': 'Link modules from external repositories',
|
'summary': 'Link modules from external repositories',
|
||||||
'description': """
|
'description': """
|
||||||
|
|
|
||||||
|
|
@ -17,22 +17,27 @@
|
||||||
# DEALINGS IN THE SOFTWARE.
|
# DEALINGS IN THE SOFTWARE.
|
||||||
#
|
#
|
||||||
{
|
{
|
||||||
'name': 'bemade_multiple_billing_contacts',
|
'name': 'Multiple Billing Contacts',
|
||||||
'version': '15.0.1.0.1',
|
'version': '17.0.1.0.1',
|
||||||
'summary': 'Send invoices to multiple contacts by default.',
|
'summary': 'Send invoices to multiple contacts by default.',
|
||||||
'description': """By default, newly created invoices add all invoice addresses for the given partner as
|
'description': """
|
||||||
followers on the invoice. If billing contacts are set manually on the sales order, those billing
|
By default, newly created invoices add all invoice addresses for the given partner as
|
||||||
contacts are added as followers on the invoice instead.""",
|
followers on the invoice. If billing contacts are set manually on the sales order, those billing
|
||||||
|
contacts are added as followers on the invoice instead.
|
||||||
|
""",
|
||||||
'category': 'Invoicing Management',
|
'category': 'Invoicing Management',
|
||||||
'author': 'Bemade Inc.',
|
'author': 'Bemade Inc.',
|
||||||
'website': 'https://www.bemade.org',
|
'website': 'https://www.bemade.org',
|
||||||
'license': 'OPL-1',
|
'license': 'OPL-1',
|
||||||
'depends': ['sale',
|
'depends': [
|
||||||
'account',
|
'sale',
|
||||||
'bemade_partner_root_ancestor',
|
'account',
|
||||||
],
|
'bemade_partner_root_ancestor',
|
||||||
'data': ['views/account_move_views.xml',
|
],
|
||||||
'views/res_partner_views.xml'],
|
'data': [
|
||||||
|
'views/account_move_views.xml',
|
||||||
|
'views/res_partner_views.xml'
|
||||||
|
],
|
||||||
'demo': [],
|
'demo': [],
|
||||||
'installable': True,
|
'installable': True,
|
||||||
'auto_install': False,
|
'auto_install': False,
|
||||||
|
|
|
||||||
|
|
@ -4,11 +4,13 @@ from odoo import models, fields, api
|
||||||
class AccountMove(models.Model):
|
class AccountMove(models.Model):
|
||||||
_inherit = 'account.move'
|
_inherit = 'account.move'
|
||||||
|
|
||||||
billing_contacts = fields.Many2many(comodel_name='res.partner',
|
billing_contacts = fields.Many2many(
|
||||||
string="Billing Contacts",
|
comodel_name='res.partner',
|
||||||
compute='_compute_billing_contacts',
|
string="Billing Contacts",
|
||||||
inverse='_inverse_billing_contacts',
|
compute='_compute_billing_contacts',
|
||||||
store=True,)
|
inverse='_inverse_billing_contacts',
|
||||||
|
store=True
|
||||||
|
)
|
||||||
|
|
||||||
@api.depends('line_ids.sale_line_ids.order_id', 'partner_id')
|
@api.depends('line_ids.sale_line_ids.order_id', 'partner_id')
|
||||||
def _compute_billing_contacts(self):
|
def _compute_billing_contacts(self):
|
||||||
|
|
|
||||||
|
|
@ -4,12 +4,17 @@ from odoo import models, fields, api, _, Command
|
||||||
class Partner(models.Model):
|
class Partner(models.Model):
|
||||||
_inherit = 'res.partner'
|
_inherit = 'res.partner'
|
||||||
|
|
||||||
billing_contacts = fields.Many2many(string='Default Billing Contacts',
|
billing_contacts = fields.Many2many(
|
||||||
comodel_name='res.partner',
|
string='Default Billing Contacts',
|
||||||
compute='_compute_billing_contacts',
|
comodel_name='res.partner',
|
||||||
inverse='_inverse_billing_contacts')
|
compute='_compute_billing_contacts',
|
||||||
potential_billing_contacts = fields.Many2many(comodel_name='res.partner',
|
inverse='_inverse_billing_contacts'
|
||||||
compute='_compute_billing_contacts')
|
)
|
||||||
|
|
||||||
|
potential_billing_contacts = fields.Many2many(
|
||||||
|
comodel_name='res.partner',
|
||||||
|
compute='_compute_billing_contacts'
|
||||||
|
)
|
||||||
|
|
||||||
@api.depends('child_ids.type')
|
@api.depends('child_ids.type')
|
||||||
def _compute_billing_contacts(self):
|
def _compute_billing_contacts(self):
|
||||||
|
|
|
||||||
|
|
@ -4,11 +4,13 @@ from odoo import models, fields, api, _, Command
|
||||||
class SaleOrder(models.Model):
|
class SaleOrder(models.Model):
|
||||||
_inherit = 'sale.order'
|
_inherit = 'sale.order'
|
||||||
|
|
||||||
billing_contacts = fields.Many2many(comodel_name='res.partner',
|
billing_contacts = fields.Many2many(
|
||||||
string='Billing Contacts',
|
comodel_name='res.partner',
|
||||||
compute='_compute_billing_contacts',
|
string='Billing Contacts',
|
||||||
inverse='_inverse_billing_contacts',
|
compute='_compute_billing_contacts',
|
||||||
store=True)
|
inverse='_inverse_billing_contacts',
|
||||||
|
store=True
|
||||||
|
)
|
||||||
|
|
||||||
@api.depends('partner_id')
|
@api.depends('partner_id')
|
||||||
def _compute_billing_contacts(self):
|
def _compute_billing_contacts(self):
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,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="billing_contacts"
|
<field name="billing_contacts"
|
||||||
attrs="{'invisible': [('move_type', 'not in',
|
invisible="move_type not in ('out_invoice', 'out_refund')"
|
||||||
('out_invoice', 'out_refund'))]}"
|
|
||||||
widget="many2many_checkboxes"
|
widget="many2many_checkboxes"
|
||||||
domain="['|', '|', ('id', 'in', billing_contacts),
|
domain="['|', '|', ('id', 'in', billing_contacts),
|
||||||
('parent_id', '=', partner_id),
|
('parent_id', '=', partner_id),
|
||||||
|
|
|
||||||
0
bemade_odoo2odoo_project/__init__.py
Normal file
0
bemade_odoo2odoo_project/__init__.py
Normal file
21
bemade_odoo2odoo_project/__manifest__.py
Normal file
21
bemade_odoo2odoo_project/__manifest__.py
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<odoo>
|
||||||
|
<data>
|
||||||
|
<!-- Héritage de la vue formulaire du projet pour ajuster les champs de synchronisation -->
|
||||||
|
<record id="view_project_project_form_inherit_sync" model="ir.ui.view">
|
||||||
|
<field name="name">project.project.form.inherit.sync.odoo17</field>
|
||||||
|
<field name="model">project.project</field>
|
||||||
|
<field name="inherit_id" ref="project.view_project"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//sheet" position="inside">
|
||||||
|
<!-- Ajout d'un nouveau séparateur pour les champs de synchronisation -->
|
||||||
|
<group string="Synchronisation Odoo">
|
||||||
|
<field name="customer_odoo_server"/>
|
||||||
|
<field name="customer_username" invisible="customer_odoo_server == False"/>
|
||||||
|
<field name="customer_password" invisible="customer_odoo_server == False"/>
|
||||||
|
</group>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
10
bemade_odoo2odoo_project/data/crons.xml
Normal file
10
bemade_odoo2odoo_project/data/crons.xml
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
<record id="cron_sync_projects" model="ir.cron">
|
||||||
|
<field name="name">Synchroniser les Projets avec Bébé</field>
|
||||||
|
<field name="model_id" ref="model_project_project"/>
|
||||||
|
<field name="state">code</field>
|
||||||
|
<field name="code">model.sync_projects_with_baby()</field>
|
||||||
|
<field name="interval_number">1</field>
|
||||||
|
<field name="interval_type">hours</field>
|
||||||
|
<field name="numbercall">-1</field>
|
||||||
|
<field name="active">True</field>
|
||||||
|
</record>
|
||||||
1
bemade_odoo2odoo_project/models/__init__.py
Normal file
1
bemade_odoo2odoo_project/models/__init__.py
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
from . import project_project
|
||||||
47
bemade_odoo2odoo_project/models/odoo2odoo_sync.py
Normal file
47
bemade_odoo2odoo_project/models/odoo2odoo_sync.py
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
from odoo import models, fields, api
|
||||||
|
|
||||||
|
class Odoo2OdooSync(models.Model):
|
||||||
|
_name = 'odoo2odoo.sync'
|
||||||
|
_description = 'Synchronisation Odoo à Odoo'
|
||||||
|
|
||||||
|
model_id = fields.Many2one(
|
||||||
|
comodel_name='ir.model',
|
||||||
|
string='Modèle Cible',
|
||||||
|
required=True,
|
||||||
|
help="Le modèle Odoo cible de la synchronisation."
|
||||||
|
)
|
||||||
|
|
||||||
|
res_id = fields.Integer(
|
||||||
|
string='ID Ressource',
|
||||||
|
required=True,
|
||||||
|
help="L'ID de la ressource cible dans le modèle spécifié."
|
||||||
|
)
|
||||||
|
|
||||||
|
json_rpc_request = fields.Text(
|
||||||
|
string='Requête JSON RPC',
|
||||||
|
required=True,
|
||||||
|
help="Le corps complet de la requête JSON RPC pour la synchronisation."
|
||||||
|
)
|
||||||
|
|
||||||
|
state = fields.Selection(
|
||||||
|
selection=[
|
||||||
|
('draft', 'Brouillon'),
|
||||||
|
('success', 'Succès'),
|
||||||
|
('failed', 'Échoué')
|
||||||
|
],
|
||||||
|
string='État',
|
||||||
|
default='draft',
|
||||||
|
required=True,
|
||||||
|
help="L'état de la tentative de synchronisation."
|
||||||
|
)
|
||||||
|
|
||||||
|
last_attempt = fields.Datetime(
|
||||||
|
string='Dernière Tentative',
|
||||||
|
help="La date et l'heure de la dernière tentative de synchronisation."
|
||||||
|
)
|
||||||
|
|
||||||
|
confirmation = fields.Datetime(
|
||||||
|
string='Confirmation',
|
||||||
|
help="La date et l'heure de la confirmation de la synchronisation réussie."
|
||||||
|
)
|
||||||
|
|
||||||
92
bemade_odoo2odoo_project/models/project_project.py
Normal file
92
bemade_odoo2odoo_project/models/project_project.py
Normal file
|
|
@ -0,0 +1,92 @@
|
||||||
|
from odoo import models, fields, api
|
||||||
|
|
||||||
|
class Project(models.Model):
|
||||||
|
_inherit = 'project.project' # Héritage du modèle projet existant
|
||||||
|
|
||||||
|
customer_project_id = fields.Integer(
|
||||||
|
string="ID Projet Client",
|
||||||
|
required=False,
|
||||||
|
help="L'ID du projet dans le système client."
|
||||||
|
)
|
||||||
|
|
||||||
|
customer_odoo_server = fields.Char(string="Serveur Odoo Client")
|
||||||
|
customer_username = fields.Char(string="Nom d'usager Client")
|
||||||
|
customer_password = fields.Char(string="Mot de passe Client", invisible=True)
|
||||||
|
|
||||||
|
odoo2odoo_sync_ids = fields.One2many(
|
||||||
|
comodel_name='odoo2odoo.sync',
|
||||||
|
inverse_name='project_id',
|
||||||
|
string='Synchronisations'
|
||||||
|
)
|
||||||
|
|
||||||
|
last_push = fields.Datetime(
|
||||||
|
string="Dernière Tentative de Synchronisation",
|
||||||
|
compute="_compute_sync_status",
|
||||||
|
store=True
|
||||||
|
)
|
||||||
|
|
||||||
|
last_push_completed = fields.Datetime(
|
||||||
|
string="Dernière Synchronisation Réussie",
|
||||||
|
compute="_compute_sync_status",
|
||||||
|
store=True
|
||||||
|
)
|
||||||
|
|
||||||
|
last_updated = fields.Datetime(
|
||||||
|
string="Dernière Mise à Jour par Bébé",
|
||||||
|
ompute="_compute_sync_status",
|
||||||
|
store=True
|
||||||
|
)
|
||||||
|
|
||||||
|
odoo2odoo_sync_count = fields.Integer(
|
||||||
|
string="Nombre de Synchronisations",
|
||||||
|
compute="_compute_sync_status"
|
||||||
|
)
|
||||||
|
|
||||||
|
odoo2odoo_sync_completed_count = fields.Integer(
|
||||||
|
string="Nombre de Synchronisations Réussies",
|
||||||
|
compute="_compute_sync_status"
|
||||||
|
)
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def create(self, vals):
|
||||||
|
# Appel de la méthode super() pour créer le projet dans Odoo.
|
||||||
|
new_project = super(Project, self).create(vals)
|
||||||
|
|
||||||
|
if 'customer_odoo_server' in vals and vals['customer_odoo_server']:
|
||||||
|
# Simulation de données pour la synchronisation avec le système "bébé".
|
||||||
|
# Vous adapterez cette partie selon votre logique spécifique de synchronisation.
|
||||||
|
json_rpc_request = json.dumps({
|
||||||
|
"jsonrpc": "2.0",
|
||||||
|
"method": "create_project",
|
||||||
|
"params": {
|
||||||
|
"name": new_project.name,
|
||||||
|
# Ajoutez d'autres paramètres nécessaires pour la création du projet dans le système "bébé".
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
# Création d'un enregistrement dans odoo2odoo.sync pour suivre la tentative de synchronisation.
|
||||||
|
self.env['odoo2odoo.sync'].create({
|
||||||
|
'model_id': self.env.ref('base.model_project_project').id,
|
||||||
|
'res_id': new_project.id,
|
||||||
|
'json_rpc_request': json_rpc_request,
|
||||||
|
'state': 'draft', # Commencez avec l'état 'draft' pour la nouvelle synchronisation.
|
||||||
|
})
|
||||||
|
return new_project
|
||||||
|
|
||||||
|
@api.depends('odoo2odoo_sync_ids.state')
|
||||||
|
def _compute_sync_status(self):
|
||||||
|
for project in self:
|
||||||
|
sync_records = self.env['odoo2odoo.sync'].search([
|
||||||
|
('model_id.model', '=', 'project.project'),
|
||||||
|
('res_id', '=', project.id),
|
||||||
|
], order='last_attempt desc')
|
||||||
|
project.odoo2odoo_sync_count = len(sync_records)
|
||||||
|
if sync_records:
|
||||||
|
project.odoo2odoo_sync_completed_count = len(sync_records.filtered(lambda r: r.state == 'success'))
|
||||||
|
project.last_push = sync_records[0].last_attempt
|
||||||
|
success_records = sync_records.filtered(lambda r: r.state == 'success')
|
||||||
|
if success_records:
|
||||||
|
project.last_push_completed = success_records[0].last_attempt
|
||||||
|
# Assume that 'last_updated' reflects the last successful pull from 'bébé'
|
||||||
|
# This requires additional logic to track when updates are received from the bébé system
|
||||||
|
project.last_updated = success_records[0].confirmation if success_records else False
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<data>
|
||||||
|
|
||||||
|
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
{
|
{
|
||||||
'name': 'Bemade Odoo Partner Scrapper',
|
'name': 'Bemade Odoo Partner Scrapper',
|
||||||
'version': '1.0.0',
|
'version': '17.0.0.0.0',
|
||||||
'category': 'Administration',
|
'category': 'Administration',
|
||||||
'summary': 'Module for scraping partners from odoo.com.',
|
'summary': 'Module for scraping partners from odoo.com.',
|
||||||
'description': """
|
'description': """
|
||||||
|
|
@ -20,14 +20,15 @@
|
||||||
'data': [
|
'data': [
|
||||||
# No security
|
# No security
|
||||||
'views/res_partner_views.xml',
|
'views/res_partner_views.xml',
|
||||||
|
# Commented for migration to 17, raises error on update
|
||||||
'data/res_partner_relation_type.xml'
|
'data/res_partner_relation_type.xml'
|
||||||
],
|
],
|
||||||
"assets": {
|
# "assets": {
|
||||||
"web.assets_backend": [
|
# "web.assets_backend": [
|
||||||
# "bemade_odoo_partner_scrapper/static/src/js/odoo_scrapper.js",
|
# "bemade_odoo_partner_scrapper/static/src/js/odoo_scrapper.js",
|
||||||
# "bemade_odoo_partner_scrapper/static/src/xml/odoo_scrapper_templates.xml",
|
# "bemade_odoo_partner_scrapper/static/src/xml/odoo_scrapper_templates.xml",
|
||||||
],
|
# ],
|
||||||
},
|
# },
|
||||||
'demo': [],
|
'demo': [],
|
||||||
'installable': True,
|
'installable': True,
|
||||||
'application': False,
|
'application': False,
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,9 @@
|
||||||
<record id="rel_type_odoo_partner" model="res.partner.relation.type">
|
<record id="rel_type_odoo_partner" model="res.partner.relation.type">
|
||||||
<field name="name">Is Odoo Partner Of</field>
|
<field name="name">Is Odoo Partner Of</field>
|
||||||
<field name="name_inverse">Is Odoo Client Of</field>
|
<field name="name_inverse">Is Odoo Client Of</field>
|
||||||
<field name="contact_type_left">c</field>
|
<!-- Commented two lines below due to data integrity issue when migrating to 17.0. Are they really needed? -->
|
||||||
<field name="contact_type_right">c</field>
|
<!-- <field name="contact_type_left">c</field>-->
|
||||||
|
<!-- <field name="contact_type_right">c</field>-->
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<!-- Categories -->
|
<!-- Categories -->
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@
|
||||||
<field name="is_odoo_user"/>Odoo User
|
<field name="is_odoo_user"/>Odoo User
|
||||||
<field name="is_odoo_partner"/>Odoo Partner
|
<field name="is_odoo_partner"/>Odoo Partner
|
||||||
<br/>
|
<br/>
|
||||||
<div attrs="{'invisible': [('is_odoo_partner', '=', False)]}">
|
<div invisible="is_odoo_partner == False">
|
||||||
<h3><field name="odoo_partner_type"/> Partner</h3>
|
<h3><field name="odoo_partner_type"/> Partner</h3>
|
||||||
</div>
|
</div>
|
||||||
</field>
|
</field>
|
||||||
|
|
@ -60,10 +60,10 @@
|
||||||
<xpath expr="//kanban" position="inside">
|
<xpath expr="//kanban" position="inside">
|
||||||
<field name="odoo_partner_type"/>
|
<field name="odoo_partner_type"/>
|
||||||
</xpath>
|
</xpath>
|
||||||
<xpath expr="//div[@class='oe_kanban_global_click o_kanban_record_has_image_fill o_res_partner_kanban']" position="attributes">
|
<xpath expr="//div[hasclass('oe_kanban_global_click') and hasclass('o_kanban_record_has_image_fill') and hasclass('o_res_partner_kanban')]" position="attributes">
|
||||||
<attribute name="t-attf-class">oe_kanban_global_click o_kanban_record_has_image_fill o_res_partner_kanban oe_kanban_color_#{record.color}</attribute>
|
<attribute name="t-attf-class">oe_kanban_global_click o_kanban_record_has_image_fill o_res_partner_kanban oe_kanban_color_#{record.color}</attribute>
|
||||||
</xpath>
|
</xpath>
|
||||||
<xpath expr="//strong[@class='o_kanban_record_title oe_partner_heading']" position="before">
|
<xpath expr="//strong[hasclass('o_kanban_record_title') and hasclass('oe_partner_heading')]" position="before">
|
||||||
<strong t-if="record.odoo_partner_type.raw_value" class="o_kanban_record_subtitle oe_partner_heading">
|
<strong t-if="record.odoo_partner_type.raw_value" class="o_kanban_record_subtitle oe_partner_heading">
|
||||||
<field name="odoo_partner_type"/>
|
<field name="odoo_partner_type"/>
|
||||||
</strong>
|
</strong>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
{
|
{
|
||||||
'name': 'Bemade Odoo Partner Scrapper JS Only',
|
'name': 'Bemade Odoo Partner Scrapper JS Only',
|
||||||
'version': '1.0.0',
|
'version': '17.0.1.0.0',
|
||||||
'category': 'Administration',
|
'category': 'Administration',
|
||||||
'summary': 'Module for scraping partners from odoo.com.',
|
'summary': 'Module for scraping partners from odoo.com.',
|
||||||
'description': """
|
'description': """
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
{
|
{
|
||||||
'name': 'Packing wizard',
|
'name': 'Packing wizard',
|
||||||
'version': '15.0.1.0.0',
|
'version': '17.0.1.0.0',
|
||||||
'category': 'Extra Tools',
|
'category': 'Extra Tools',
|
||||||
'summary': 'Allow automated packing type creation',
|
'summary': 'Allow automated packing type creation',
|
||||||
'description': """
|
'description': """
|
||||||
This module allows users simply enter all the package dimensions on every packing and it will create the packing
|
This module allows users simply enter all the package dimensions on every packing and it will create the packing
|
||||||
type automatically if it not exists, use it the create the package with the weight and related package type.
|
type automatically if it not exists, use it the create the package with the weight and related package type.
|
||||||
|
|
||||||
You need to activate the auto create package feature on the delivery carrier to use this feature.
|
You need to activate the auto create package feature on the delivery carrier to use this feature.
|
||||||
""",
|
""",
|
||||||
'depends': [
|
'depends': [
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,7 @@ class ChooseDeliveryPackage(models.TransientModel):
|
||||||
# if no package type found, create one
|
# if no package type found, create one
|
||||||
if not delivery_package_type:
|
if not delivery_package_type:
|
||||||
delivery_package_type = delivery_package_type.create({
|
delivery_package_type = delivery_package_type.create({
|
||||||
'name': f'Box {vals["width"]}x{vals["height"]}x{vals["length"]}',
|
'name': f'Box {self.width}x{self.height}x{self.length}',
|
||||||
'width': self.width,
|
'width': self.width,
|
||||||
'height': self.height,
|
'height': self.height,
|
||||||
'packaging_length': self.length,
|
'packaging_length': self.length,
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
'name': 'Automated Partner Association by Email Domain',
|
'name': 'Automated Partner Association by Email Domain',
|
||||||
'version': '15.0.0.0.0',
|
'version': '17.0.0.0.0',
|
||||||
'category': 'Extra Tools',
|
'category': 'Extra Tools',
|
||||||
'summary': 'Automatically associates partners with companies using matching email domains',
|
'summary': 'Automatically associates partners with companies using matching email domains',
|
||||||
'description': """
|
'description': """
|
||||||
|
|
|
||||||
|
|
@ -8,13 +8,13 @@
|
||||||
<field name="partner_to">{{object.id}}</field>
|
<field name="partner_to">{{object.id}}</field>
|
||||||
<field name="subject">Select Your Division at {{object.company_id.name}}</field>
|
<field name="subject">Select Your Division at {{object.company_id.name}}</field>
|
||||||
<field name="body_html" type="html">
|
<field name="body_html" type="html">
|
||||||
<table border="0" cellpadding="0" cellspacing="0"
|
<table border="0" cellpadding="0" cellspacing="0"
|
||||||
style="padding-top: 16px; background-color: #F1F1F1; font-family:Verdana, Arial, sans-serif; color: #454748; width: 100%; border-collapse:separate;">
|
style="padding-top: 16px; background-color: #F1F1F1; font-family:Verdana, Arial, sans-serif; color: #454748; width: 100%; border-collapse:separate;">
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<td align="center">
|
<td align="center">
|
||||||
<table border="0" cellpadding="0" cellspacing="0" width="590"
|
<table border="0" cellpadding="0" cellspacing="0" width="590"
|
||||||
style="padding: 16px; background-color: white; color: #454748; border-collapse:separate;">
|
style="padding: 16px; background-color: white; color: #454748; border-collapse:separate;">
|
||||||
<tbody>
|
<tbody>
|
||||||
<!-- HEADER -->
|
<!-- HEADER -->
|
||||||
<tr>
|
<tr>
|
||||||
|
|
@ -25,8 +25,8 @@
|
||||||
<!-- CONTENT -->
|
<!-- CONTENT -->
|
||||||
<tr>
|
<tr>
|
||||||
<td align="center" style="min-width: 590px;">
|
<td align="center" style="min-width: 590px;">
|
||||||
<table border="0" cellpadding="0" cellspacing="0" width="590"
|
<table border="0" cellpadding="0" cellspacing="0" width="590"
|
||||||
style="min-width: 590px; background-color: white; padding: 0px 8px 0px 8px; border-collapse:separate;">
|
style="min-width: 590px; background-color: white; padding: 0px 8px 0px 8px; border-collapse:separate;">
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<td valign="top" style="font-size: 13px;">
|
<td valign="top" style="font-size: 13px;">
|
||||||
|
|
@ -55,8 +55,8 @@
|
||||||
<!-- FOOTER -->
|
<!-- FOOTER -->
|
||||||
<tr>
|
<tr>
|
||||||
<td align="center" style="min-width: 590px;">
|
<td align="center" style="min-width: 590px;">
|
||||||
<table border="0" cellpadding="0" cellspacing="0" width="590"
|
<table border="0" cellpadding="0" cellspacing="0" width="590"
|
||||||
style="min-width: 590px; background-color: white; font-size: 11px; padding: 0px 8px 0px 8px; border-collapse:separate;">
|
style="min-width: 590px; background-color: white; font-size: 11px; padding: 0px 8px 0px 8px; border-collapse:separate;">
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<td valign="middle" align="left">
|
<td valign="middle" align="left">
|
||||||
|
|
@ -104,4 +104,3 @@
|
||||||
</record>
|
</record>
|
||||||
</data>
|
</data>
|
||||||
</odoo>
|
</odoo>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,4 +14,4 @@
|
||||||
</t>
|
</t>
|
||||||
</template>
|
</template>
|
||||||
</data>
|
</data>
|
||||||
</odoo>
|
</odoo>
|
||||||
|
|
|
||||||
|
|
@ -6,15 +6,14 @@
|
||||||
<field name="inherit_id" ref="base.view_partner_form"/>
|
<field name="inherit_id" ref="base.view_partner_form"/>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<field name="company_type" position="after">
|
<field name="company_type" position="after">
|
||||||
<label for="is_subdivision" string="Division" attrs="{'invisible': [('is_company', '=', False)]}"/>
|
<label for="is_subdivision" string="Division" invisible="is_company == False"/>
|
||||||
<field name="is_subdivision" attrs="{'invisible': [('is_company', '=', False)]}"/>
|
<field name="is_subdivision" invisible="is_company == False"/>
|
||||||
</field>
|
</field>
|
||||||
<field name="website" position="before">
|
<field name="website" position="before">
|
||||||
<field name="email_domain" attrs="{'invisible': [('is_company', '=', False)]}"/>
|
<field name="email_domain" invisible="is_company == False"/>
|
||||||
</field>
|
</field>
|
||||||
<field name="parent_id" position="attributes">
|
<field name="parent_id" position="attributes">
|
||||||
<attribute name="attrs">{'invisible': [('is_company','=', True),('is_subdivision','=', False)]}
|
<attribute name="invisible">is_company == True and is_subdivision == False</attribute>
|
||||||
</attribute>
|
|
||||||
</field>
|
</field>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@
|
||||||
#
|
#
|
||||||
{
|
{
|
||||||
'name': 'Partner Root Ancestor',
|
'name': 'Partner Root Ancestor',
|
||||||
'version': '15.0.1.0.0',
|
'version': '17.0.1.0.0',
|
||||||
'summary': 'Technical module to add the field root_ancestor to res.partner.',
|
'summary': 'Technical module to add the field root_ancestor to res.partner.',
|
||||||
'category': 'Generic Modules/Base',
|
'category': 'Generic Modules/Base',
|
||||||
'author': 'Bemade Inc.',
|
'author': 'Bemade Inc.',
|
||||||
|
|
|
||||||
|
|
@ -4,11 +4,13 @@ from odoo import models, fields, api, _, Command
|
||||||
class Partner(models.Model):
|
class Partner(models.Model):
|
||||||
_inherit = 'res.partner'
|
_inherit = 'res.partner'
|
||||||
|
|
||||||
root_ancestor = fields.Many2one(comodel_name='res.partner',
|
root_ancestor = fields.Many2one(
|
||||||
string='Root Ancestor',
|
comodel_name='res.partner',
|
||||||
compute='_compute_root_ancestor',
|
string='Root Ancestor',
|
||||||
store=True,
|
compute='_compute_root_ancestor',
|
||||||
recursive=True)
|
store=True,
|
||||||
|
recursive=True
|
||||||
|
)
|
||||||
|
|
||||||
@api.depends('parent_id', 'parent_id.root_ancestor')
|
@api.depends('parent_id', 'parent_id.root_ancestor')
|
||||||
def _compute_root_ancestor(self):
|
def _compute_root_ancestor(self):
|
||||||
|
|
|
||||||
0
bemade_payslip_done_as_not_paid/__init__.py
Normal file
0
bemade_payslip_done_as_not_paid/__init__.py
Normal file
31
bemade_payslip_done_as_not_paid/__manifest__.py
Normal file
31
bemade_payslip_done_as_not_paid/__manifest__.py
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
#
|
||||||
|
# Bemade Inc.
|
||||||
|
#
|
||||||
|
# Copyright (C) 2023-June Bemade Inc. (<https://www.bemade.org>).
|
||||||
|
# Author: Marc Durepos (Contact : marc@bemade.org)
|
||||||
|
#
|
||||||
|
# This program is under the terms of the Odoo Proprietary License v1.0 (OPL-1)
|
||||||
|
# It is forbidden to publish, distribute, sublicense, or sell copies of the Software
|
||||||
|
# or modified copies of the Software.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||||
|
# DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||||
|
# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
# DEALINGS IN THE SOFTWARE.
|
||||||
|
#
|
||||||
|
{
|
||||||
|
'name': 'Payslip Done as To Pay',
|
||||||
|
'version': '17.0.1.0.0',
|
||||||
|
'summary': 'Payslips in "Done" state appear as "To Pay"',
|
||||||
|
'category': 'Human Resources/Payroll',
|
||||||
|
'author': 'Bemade Inc.',
|
||||||
|
'website': 'http://www.bemade.org',
|
||||||
|
'license': 'OPL-1',
|
||||||
|
'depends': ['hr_payroll'],
|
||||||
|
'data': ['views/hr_payslip_views.xml'],
|
||||||
|
'installable': True,
|
||||||
|
'auto_install': True,
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<odoo>
|
||||||
|
<record id="hr_payroll.hr_payslip_action_view_to_pay" model="ir.actions.act_window">
|
||||||
|
<field name="domain">[('state', 'in', ['draft', 'verify', 'done'])]</field>
|
||||||
|
</record>
|
||||||
|
</odoo>
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
{
|
{
|
||||||
"name": "Chatter on the Reordering Rules",
|
"name": "Chatter on the Reordering Rules",
|
||||||
"version": "15.0.0.0.1",
|
"version": "17.0.0.0.1",
|
||||||
"category": "Extra Tools",
|
"category": "Extra Tools",
|
||||||
'license': 'GPL-3',
|
'license': 'GPL-3',
|
||||||
'summary': 'Add chatter on the reordering rules',
|
'summary': 'Add chatter on the reordering rules',
|
||||||
|
|
@ -15,7 +15,7 @@
|
||||||
],
|
],
|
||||||
"data": [
|
"data": [
|
||||||
# BV : FOR MIGRATION
|
# BV : FOR MIGRATION
|
||||||
# 'views/stock_warehouse_orderpoint.xml',
|
'views/stock_warehouse_orderpoint.xml',
|
||||||
],
|
],
|
||||||
"auto_install": False,
|
"auto_install": False,
|
||||||
"installable": True,
|
"installable": True,
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
<record id="view_warehouse_orderpoint_tree_editable_config_chatter" model="ir.ui.view">
|
<record id="view_warehouse_orderpoint_tree_editable_config_chatter" model="ir.ui.view">
|
||||||
<field name="name">stock.warehouse.orderpoint.tree.editable.config.chatter</field>
|
<field name="name">stock.warehouse.orderpoint.tree.editable.config.chatter</field>
|
||||||
<field name="model">stock.warehouse.orderpoint</field>
|
<field name="model">stock.warehouse.orderpoint</field>
|
||||||
<field name="inherit_id" ref="stock.view_warehouse_orderpoint_tree_editable_config"/>
|
<field name="inherit_id" ref="stock.view_warehouse_orderpoint_tree_editable"/>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<xpath expr="//tree" position="attributes">
|
<xpath expr="//tree" position="attributes">
|
||||||
<attribute name="editable"/>
|
<attribute name="editable"/>
|
||||||
|
|
@ -19,8 +19,8 @@
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<xpath expr="//form/sheet" position="after">
|
<xpath expr="//form/sheet" position="after">
|
||||||
<div class="oe_chatter">
|
<div class="oe_chatter">
|
||||||
<field name="message_follower_ids" widget="mail_followers" />
|
<field name="message_follower_ids"/>
|
||||||
<field name="message_ids" widget="mail_thread" />
|
<field name="message_ids"/>
|
||||||
</div>
|
</div>
|
||||||
</xpath>
|
</xpath>
|
||||||
</field>
|
</field>
|
||||||
|
|
|
||||||
5
bemade_search_supplier_code/__init__.py
Normal file
5
bemade_search_supplier_code/__init__.py
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from . import models
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue