merge 15.0 into 17.0
This commit is contained in:
commit
30b3e2910d
34 changed files with 406 additions and 236 deletions
0
bemade_fix_quality_report/__init__.py
Normal file
0
bemade_fix_quality_report/__init__.py
Normal file
33
bemade_fix_quality_report/__manifest__.py
Normal file
33
bemade_fix_quality_report/__manifest__.py
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
#
|
||||||
|
# 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': 'Fix Quality Worksheet',
|
||||||
|
'version': '15.0.1.0.0',
|
||||||
|
'summary': 'Fix Quality worksheet bug from Odoo Enterprise',
|
||||||
|
'description': '',
|
||||||
|
'category': 'Quality Control',
|
||||||
|
'author': 'Bemade Inc.',
|
||||||
|
'website': 'http://www.bemade.org',
|
||||||
|
'license': 'OPL-1',
|
||||||
|
'depends': ['quality_control'],
|
||||||
|
'data': ['reports/worksheet_custom_report_templates.xml'],
|
||||||
|
'assets': {},
|
||||||
|
'installable': True,
|
||||||
|
'auto_install': True,
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<odoo>
|
||||||
|
<template id="worksheet_page" inherit_id="quality_control.worksheet_page">
|
||||||
|
<xpath expr="//span[@t-field='doc.result']" position="replace">
|
||||||
|
<span t-field="doc.measure"/>
|
||||||
|
</xpath>
|
||||||
|
</template>
|
||||||
|
</odoo>
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
{
|
{
|
||||||
'name': 'Mailcow Integration',
|
'name': 'Mailcow Integration',
|
||||||
'version': '1.0.0',
|
'version': '15.0.1.0.1',
|
||||||
'category': 'Administration',
|
'category': 'Administration',
|
||||||
'summary': 'Module for integrating Mailcow email server with Odoo.',
|
'summary': 'Module for integrating Mailcow email server with Odoo.',
|
||||||
'description': """
|
'description': """
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
from odoo import api, SUPERUSER_ID
|
||||||
|
|
||||||
|
|
||||||
|
def migrate(cr, version):
|
||||||
|
env = api.Environment(cr, SUPERUSER_ID, {})
|
||||||
|
settings = env['ir.config_parameter']
|
||||||
|
create_param = settings.get_param('mailcow.create_mailbox')
|
||||||
|
sync_param = settings.get_param('mailcow.sync_alias')
|
||||||
|
base_url = settings.get_param('mailcow.base_url')
|
||||||
|
api_key = settings.get_param('mailcow.api_key')
|
||||||
|
|
||||||
|
if (create_param or sync_param) and (not base_url or not api_key):
|
||||||
|
(create_param | sync_param).write({'value': False})
|
||||||
|
|
@ -6,6 +6,7 @@ class MailAlias(models.Model):
|
||||||
|
|
||||||
mailcow_id = fields.One2many('mail.mailcow.alias', 'alias_id')
|
mailcow_id = fields.One2many('mail.mailcow.alias', 'alias_id')
|
||||||
|
|
||||||
|
|
||||||
@api.model_create_multi
|
@api.model_create_multi
|
||||||
def create(self, vals_list):
|
def create(self, vals_list):
|
||||||
alias_list = super().create(vals_list)
|
alias_list = super().create(vals_list)
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
from odoo import fields, models
|
from odoo import fields, models, api, _
|
||||||
|
from odoo.exceptions import ValidationError
|
||||||
|
|
||||||
|
|
||||||
class ResConfigSettings(models.TransientModel):
|
class ResConfigSettings(models.TransientModel):
|
||||||
_inherit = 'res.config.settings'
|
_inherit = 'res.config.settings'
|
||||||
|
|
@ -23,3 +25,8 @@ class ResConfigSettings(models.TransientModel):
|
||||||
string='Create Mailboxes in Mailcow',
|
string='Create Mailboxes in Mailcow',
|
||||||
help='Auto create Mailboxes in Mailcow on creation in Odoo',
|
help='Auto create Mailboxes in Mailcow on creation in Odoo',
|
||||||
config_parameter='mailcow.create_mailbox')
|
config_parameter='mailcow.create_mailbox')
|
||||||
|
|
||||||
|
@api.constrains('mailcow_sync_alias', 'mailcow_auto_create')
|
||||||
|
def require_api_key_and_base_url_to_sync(self):
|
||||||
|
if not self.mailcow_api_key and self.mailcow_base_url:
|
||||||
|
raise ValidationError(_("You must set a base URL and API key to enable synchronization."))
|
||||||
|
|
|
||||||
|
|
@ -29,14 +29,14 @@
|
||||||
'author': 'Bemade Inc.',
|
'author': 'Bemade Inc.',
|
||||||
'website': 'https://www.bemade.org',
|
'website': 'https://www.bemade.org',
|
||||||
'license': 'OPL-1',
|
'license': 'OPL-1',
|
||||||
'depends': [
|
'depends': ['sale',
|
||||||
'sale',
|
'account',
|
||||||
'account',
|
'bemade_partner_root_ancestor',
|
||||||
'bemade_partner_root_ancestor',
|
],
|
||||||
],
|
|
||||||
'data': [
|
'data': [
|
||||||
'views/account_move_views.xml',
|
'views/account_move_views.xml',
|
||||||
'views/res_partner_views.xml'
|
'views/res_partner_views.xml',
|
||||||
|
'views/sale_order_views.xml',
|
||||||
],
|
],
|
||||||
'demo': [],
|
'demo': [],
|
||||||
'installable': True,
|
'installable': True,
|
||||||
|
|
|
||||||
20
bemade_multiple_billing_contacts/views/sale_order_views.xml
Normal file
20
bemade_multiple_billing_contacts/views/sale_order_views.xml
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<odoo>
|
||||||
|
<record id="view_sale_order_form_inherit" model="ir.ui.view">
|
||||||
|
<field name="name">view_sale_order_form_inherit</field>
|
||||||
|
<field name="model">sale.order</field>
|
||||||
|
<field name="inherit_id" ref="sale.view_order_form"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<field name="partner_invoice_id" position="after">
|
||||||
|
<field name="partner_id" position="after">
|
||||||
|
<field name="billing_contacts"
|
||||||
|
widget="many2many_checkboxes"
|
||||||
|
domain="['|', '|', ('id', 'in', billing_contacts),
|
||||||
|
('parent_id', '=', partner_invoice_id),
|
||||||
|
('parent_id', 'parent_of', partner_invoice_id),
|
||||||
|
('is_company', '=', False)]"/>
|
||||||
|
</field>
|
||||||
|
</field>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
</odoo>
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from . import wizard
|
from . import wizard
|
||||||
from . import models
|
from . import models
|
||||||
|
|
|
||||||
|
|
@ -26,9 +26,9 @@
|
||||||
|
|
||||||
# always loaded
|
# always loaded
|
||||||
'data': [
|
'data': [
|
||||||
# 'security/ir.model.access.csv',
|
'security/ir.model.access.csv',
|
||||||
'views/sale_order_views.xml',
|
'views/sale_order_views.xml',
|
||||||
'wizard/sale_order_alternative_views.xml',
|
'wizard/sale_order_duplication_wizard_view.xml',
|
||||||
],
|
],
|
||||||
# only loaded in demonstration mode
|
# only loaded in demonstration mode
|
||||||
'demo': [
|
'demo': [
|
||||||
|
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
from . import controllers
|
|
||||||
|
|
@ -1,21 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
# from odoo import http
|
|
||||||
|
|
||||||
|
|
||||||
# class .repos/bemade-addons/bemadeQuotationAlternative(http.Controller):
|
|
||||||
# @http.route('/.repos/bemade-addons/bemade_quotation_alternative/.repos/bemade-addons/bemade_quotation_alternative', auth='public')
|
|
||||||
# def index(self, **kw):
|
|
||||||
# return "Hello, world"
|
|
||||||
|
|
||||||
# @http.route('/.repos/bemade-addons/bemade_quotation_alternative/.repos/bemade-addons/bemade_quotation_alternative/objects', auth='public')
|
|
||||||
# def list(self, **kw):
|
|
||||||
# return http.request.render('.repos/bemade-addons/bemade_quotation_alternative.listing', {
|
|
||||||
# 'root': '/.repos/bemade-addons/bemade_quotation_alternative/.repos/bemade-addons/bemade_quotation_alternative',
|
|
||||||
# 'objects': http.request.env['.repos/bemade-addons/bemade_quotation_alternative..repos/bemade-addons/bemade_quotation_alternative'].search([]),
|
|
||||||
# })
|
|
||||||
|
|
||||||
# @http.route('/.repos/bemade-addons/bemade_quotation_alternative/.repos/bemade-addons/bemade_quotation_alternative/objects/<model(".repos/bemade-addons/bemade_quotation_alternative..repos/bemade-addons/bemade_quotation_alternative"):obj>', auth='public')
|
|
||||||
# def object(self, obj, **kw):
|
|
||||||
# return http.request.render('.repos/bemade-addons/bemade_quotation_alternative.object', {
|
|
||||||
# 'object': obj
|
|
||||||
# })
|
|
||||||
|
|
@ -1,30 +0,0 @@
|
||||||
<odoo>
|
|
||||||
<data>
|
|
||||||
<!--
|
|
||||||
<record id="object0" model=".repos/bemade-addons/bemade_quotation_alternative..repos/bemade-addons/bemade_quotation_alternative">
|
|
||||||
<field name="name">Object 0</field>
|
|
||||||
<field name="value">0</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record id="object1" model=".repos/bemade-addons/bemade_quotation_alternative..repos/bemade-addons/bemade_quotation_alternative">
|
|
||||||
<field name="name">Object 1</field>
|
|
||||||
<field name="value">10</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record id="object2" model=".repos/bemade-addons/bemade_quotation_alternative..repos/bemade-addons/bemade_quotation_alternative">
|
|
||||||
<field name="name">Object 2</field>
|
|
||||||
<field name="value">20</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record id="object3" model=".repos/bemade-addons/bemade_quotation_alternative..repos/bemade-addons/bemade_quotation_alternative">
|
|
||||||
<field name="name">Object 3</field>
|
|
||||||
<field name="value">30</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record id="object4" model=".repos/bemade-addons/bemade_quotation_alternative..repos/bemade-addons/bemade_quotation_alternative">
|
|
||||||
<field name="name">Object 4</field>
|
|
||||||
<field name="value">40</field>
|
|
||||||
</record>
|
|
||||||
-->
|
|
||||||
</data>
|
|
||||||
</odoo>
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from . import sale_order
|
from . import sale_order
|
||||||
|
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
# from odoo import models, fields, api
|
|
||||||
|
|
||||||
|
|
||||||
# class .repos/bemade-addons/bemade_quotation_alternative(models.Model):
|
|
||||||
# _name = '.repos/bemade-addons/bemade_quotation_alternative..repos/bemade-addons/bemade_quotation_alternative'
|
|
||||||
# _description = '.repos/bemade-addons/bemade_quotation_alternative..repos/bemade-addons/bemade_quotation_alternative'
|
|
||||||
|
|
||||||
# name = fields.Char()
|
|
||||||
# value = fields.Integer()
|
|
||||||
# value2 = fields.Float(compute="_value_pc", store=True)
|
|
||||||
# description = fields.Text()
|
|
||||||
#
|
|
||||||
# @api.depends('value')
|
|
||||||
# def _value_pc(self):
|
|
||||||
# for record in self:
|
|
||||||
# record.value2 = float(record.value) / 100
|
|
||||||
|
|
@ -1,17 +1,13 @@
|
||||||
from odoo import fields, models, api
|
from odoo import fields, models, api
|
||||||
|
|
||||||
|
|
||||||
class ModelName(models.Model):
|
class SaleOrder(models.Model):
|
||||||
_inherit = 'sale.order'
|
_inherit = 'sale.order'
|
||||||
|
|
||||||
def action_create_alternative(self):
|
def action_duplicate_order(self):
|
||||||
return {
|
self.ensure_one()
|
||||||
'name': 'Create Alternative',
|
action = self.env.ref('bemade_quotation_alternative.sale_order_duplication_wizard_action').read()[0]
|
||||||
'type': 'ir.actions.act_window',
|
action['context'] = {
|
||||||
'res_model': 'bemade.quotation.alternative',
|
'default_original_order_id': self.id,
|
||||||
'view_mode': 'form',
|
}
|
||||||
'target': 'new',
|
return action
|
||||||
'context': {
|
|
||||||
'default_sale_order_id': self.id,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,2 +1,3 @@
|
||||||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||||
access_.repos/bemade-addons/bemade_quotation_alternative_.repos/bemade-addons/bemade_quotation_alternative,.repos/bemade-addons/bemade_quotation_alternative..repos/bemade-addons/bemade_quotation_alternative,model_.repos/bemade-addons/bemade_quotation_alternative_.repos/bemade-addons/bemade_quotation_alternative,base.group_user,1,1,1,1
|
bemade_quotation_alternative.access_sale_order_duplication_wizard,access_sale_order_duplication_wizard,bemade_quotation_alternative.model_sale_order_duplication_wizard,base.group_user,1,1,1,1
|
||||||
|
bemade_quotation_alternative.access_sale_order_line_duplication_wizard,access_sale_order_line_duplication_wizard,bemade_quotation_alternative.model_sale_order_line_duplication_wizard,base.group_user,1,1,1,1
|
||||||
|
|
|
||||||
|
|
|
@ -1,17 +1,40 @@
|
||||||
<odoo>
|
<odoo>
|
||||||
<data>
|
<data>
|
||||||
|
<record id="view_sale_order_form_inherit" model="ir.ui.view">
|
||||||
|
<field name="name">sale.order.form.inherit</field>
|
||||||
|
<field name="model">sale.order</field>
|
||||||
|
<field name="inherit_id" ref="sale.view_order_form"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<header>
|
||||||
|
<button name="action_duplicate_order"
|
||||||
|
string="Duplicate Order"
|
||||||
|
type="object"
|
||||||
|
class="oe_highlight"
|
||||||
|
attrs="{'invisible': [('state', '!=', 'draft')]}"
|
||||||
|
/>
|
||||||
|
</header>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<!-- explicit list view definition -->
|
<!-- explicit list view definition -->
|
||||||
|
|
||||||
<record model="ir.ui.view" id="bemade_quotation_alternative_form">
|
<!-- <record model="ir.ui.view" id="bemade_quotation_alternative_form">-->
|
||||||
<field name="name">bemade_quotation_alternative form</field>
|
<!-- <field name="name">bemade_quotation_alternative form</field>-->
|
||||||
<field name="model">sale.order</field>
|
<!-- <field name="model">sale.order</field>-->
|
||||||
<field name="inherit_id" ref="sale.view_order_form"/>
|
<!-- <field name="inherit_id" ref="sale.view_order_form"/>-->
|
||||||
<field name="arch" type="xml">
|
<!-- <field name="arch" type="xml">-->
|
||||||
<header position="inside">
|
<!-- <header position="inside">-->
|
||||||
<button name="action_create_alternative" type="object" string="Create Alternative" class="oe_highlight" />
|
<!-- <button name="action_create_alternative" type="object" string="Create Alternative" class="oe_highlight" />-->
|
||||||
</header>
|
<!-- </header>-->
|
||||||
</field>
|
<!-- </field>-->
|
||||||
</record>
|
<!-- </record>-->
|
||||||
|
|
||||||
<!-- actions opening views on models -->
|
<!-- actions opening views on models -->
|
||||||
<!--
|
<!--
|
||||||
|
|
|
||||||
|
|
@ -1,24 +0,0 @@
|
||||||
<odoo>
|
|
||||||
<data>
|
|
||||||
<!--
|
|
||||||
<template id="listing">
|
|
||||||
<ul>
|
|
||||||
<li t-foreach="objects" t-as="object">
|
|
||||||
<a t-attf-href="#{ root }/objects/#{ object.id }">
|
|
||||||
<t t-esc="object.display_name"/>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</template>
|
|
||||||
<template id="object">
|
|
||||||
<h1><t t-esc="object.display_name"/></h1>
|
|
||||||
<dl>
|
|
||||||
<t t-foreach="object._fields" t-as="field">
|
|
||||||
<dt><t t-esc="field"/></dt>
|
|
||||||
<dd><t t-esc="object[field]"/></dd>
|
|
||||||
</t>
|
|
||||||
</dl>
|
|
||||||
</template>
|
|
||||||
-->
|
|
||||||
</data>
|
|
||||||
</odoo>
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from . import sale_order_alternative
|
from . import sale_order_duplication_wizard
|
||||||
|
from . import sale_oder_line_duplication_wizard
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
from odoo import models, fields
|
||||||
|
|
||||||
|
class SaleOrderLineDuplicationWizard(models.TransientModel):
|
||||||
|
_name = 'sale.order.line.duplication.wizard'
|
||||||
|
_description = 'Wizard for selecting sale order lines to duplicate'
|
||||||
|
|
||||||
|
wizard_id = fields.Many2one('sale.order.duplication.wizard', required=True, ondelete='cascade', string="Wizard")
|
||||||
|
sale_order_line_id = fields.Many2one('sale.order.line', string="Sale Order Line", required=True)
|
||||||
|
to_duplicate = fields.Boolean(string="Duplicate?", default=True)
|
||||||
|
|
@ -1,59 +0,0 @@
|
||||||
from odoo import fields, models, api
|
|
||||||
import string
|
|
||||||
|
|
||||||
|
|
||||||
class AlternativeQuotation(models.TransientModel):
|
|
||||||
_name = 'sale.order.alternative'
|
|
||||||
_description = 'Quotation Alternative'
|
|
||||||
|
|
||||||
name = fields.Char()
|
|
||||||
original_sale_order_id = fields.Many2one('sale.order')
|
|
||||||
|
|
||||||
clone_all_lines = fields.Boolean('Clone all lines', default=True)
|
|
||||||
origin = fields.Text('Why this alternative ?')
|
|
||||||
internal_note = fields.Text('Internal Note')
|
|
||||||
|
|
||||||
def action_create_alternative(self):
|
|
||||||
name = self.original_sale_order_id.name
|
|
||||||
|
|
||||||
if name and name[-1] not in string.ascii_letters:
|
|
||||||
self.original_sale_order_id.name = name + "A"
|
|
||||||
name = self.name + "B"
|
|
||||||
else:
|
|
||||||
# Assuming 'all_names' is a list of all names
|
|
||||||
# Add a domain to filter names starting with 'name' (less the last letter)
|
|
||||||
all_names = [record.name for record in self.env['sale.order'].search([('name', 'like', name[:-1] + '%')])]
|
|
||||||
if name in all_names:
|
|
||||||
# Get the index of the current name in the list
|
|
||||||
index = all_names.index(name)
|
|
||||||
# If this is not the last name in the list, get the next one
|
|
||||||
if index < len(all_names) - 1:
|
|
||||||
name = all_names[index + 1]
|
|
||||||
else:
|
|
||||||
# If this is the last name, just add 'B' to it
|
|
||||||
name = name + "B"
|
|
||||||
|
|
||||||
new_quot = quot.original_sale_order_id.copy({
|
|
||||||
'name': quot.name,
|
|
||||||
'origin': quot.original_sale_order_id.name,
|
|
||||||
'alternative_sale_order_id': quot.original_sale_order_id.id,
|
|
||||||
'alternative': True,
|
|
||||||
'alternative_origine': quot.origine,
|
|
||||||
'alternative_internal_note': quot.internal_note,
|
|
||||||
|
|
||||||
# # Lines and line based computes
|
|
||||||
# order_line = fields.One2many(
|
|
||||||
# comodel_name='sale.order.alternative.line',
|
|
||||||
# inverse_name='order_id',
|
|
||||||
# string="Order Lines",
|
|
||||||
# copy=True, auto_join=True)
|
|
||||||
#
|
|
||||||
# class AlternativeQuotation(models.TransientModel):
|
|
||||||
# _name = 'sale.order.alternative.line'
|
|
||||||
# # _inherit = 'sale.order.line'
|
|
||||||
# _description = 'Quotation Alternative Line'
|
|
||||||
#
|
|
||||||
# order_id = fields.Many2one(
|
|
||||||
# comodel_name='sale.order.alternative',
|
|
||||||
# string="Alternative Quotation Reference",
|
|
||||||
# required=True, ondelete='cascade', index=True, copy=False)
|
|
||||||
|
|
@ -1,45 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<odoo>
|
|
||||||
<data>
|
|
||||||
|
|
||||||
<record id="sale_order_alternative_form_view" model="ir.ui.view">
|
|
||||||
<field name="name">bemade_quotation_alternative.sale_order_alternative.form</field>
|
|
||||||
<field name="model">sale.order.alternative</field>
|
|
||||||
<field name="arch" type="xml">
|
|
||||||
<form string="Alternative Quotation">
|
|
||||||
<sheet>
|
|
||||||
<div class="oe_title">
|
|
||||||
<label for="original_sale_order_id"/>
|
|
||||||
<h1>
|
|
||||||
<field name="original_sale_order_id" placeholder="Original"/>
|
|
||||||
</h1>
|
|
||||||
</div>
|
|
||||||
<group>
|
|
||||||
<group>
|
|
||||||
<field name="order_line"/>
|
|
||||||
</group>
|
|
||||||
<group>
|
|
||||||
</group>
|
|
||||||
</group>
|
|
||||||
</sheet>
|
|
||||||
</form>
|
|
||||||
</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<!-- <record id="ModelName_act_window" model="ir.actions.act_window">-->
|
|
||||||
<!-- <field name="name">ModelTitle</field>-->
|
|
||||||
<!-- <field name="type">ir.actions.act_window</field>-->
|
|
||||||
<!-- <field name="res_model">ProjectName.ModelName</field>-->
|
|
||||||
<!-- <field name="view_type">form</field>-->
|
|
||||||
<!-- <field name="view_mode">tree,form</field>-->
|
|
||||||
<!-- <field name="help" type="html">-->
|
|
||||||
<!-- <p class="oe_view_nocontent_create">-->
|
|
||||||
<!-- There is no examples click here to add new ModelTitle.-->
|
|
||||||
<!-- </p>-->
|
|
||||||
<!-- </field>-->
|
|
||||||
<!-- </record>-->
|
|
||||||
|
|
||||||
<!-- <menuitem name="ModelTitle" id="ModelName_menu" parent="your_root_menu" action="ModelName_act_window"/>-->
|
|
||||||
|
|
||||||
</data>
|
|
||||||
</odoo>
|
|
||||||
|
|
@ -0,0 +1,78 @@
|
||||||
|
from odoo import models, fields, api
|
||||||
|
|
||||||
|
class SaleOrderDuplicationWizard(models.TransientModel):
|
||||||
|
_name = 'sale.order.duplication.wizard'
|
||||||
|
_description = 'Wizard for duplicating a sale order'
|
||||||
|
|
||||||
|
original_order_id = fields.Many2one('sale.order', string='Original Order', required=True)
|
||||||
|
new_quot = fields.Char(string='New Quotation Name', compute='_compute_new_quot', store=True)
|
||||||
|
|
||||||
|
duplicate_all_lines = fields.Boolean(string='Duplicate All Lines?', default=True)
|
||||||
|
lines_to_duplicate = fields.One2many(
|
||||||
|
'sale.order.line.duplication.wizard', 'wizard_id',
|
||||||
|
string="Lines to Duplicate",
|
||||||
|
context={'default_original_order_id': original_order_id},
|
||||||
|
)
|
||||||
|
|
||||||
|
purpose = fields.Text(string='Purpose')
|
||||||
|
note = fields.Html(string='Note')
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def default_get(self, fields_list):
|
||||||
|
res = super(SaleOrderDuplicationWizard, self).default_get(fields_list)
|
||||||
|
if 'default_original_order_id' in self.env.context:
|
||||||
|
original_order_id = self.env.context['default_original_order_id']
|
||||||
|
original_order = self.env['sale.order'].browse(original_order_id)
|
||||||
|
lines_vals = []
|
||||||
|
for line in original_order.order_line:
|
||||||
|
lines_vals.append((0, 0, {'sale_order_line_id': line.id}))
|
||||||
|
res.update({
|
||||||
|
'lines_to_duplicate': lines_vals,
|
||||||
|
'purpose': original_order.purpose if 'purpose' in fields_list else '',
|
||||||
|
'note': original_order.note if 'note' in fields_list else '',
|
||||||
|
})
|
||||||
|
return res
|
||||||
|
|
||||||
|
def action_duplicate_order(self):
|
||||||
|
self.ensure_one()
|
||||||
|
# Duplication de la commande de vente
|
||||||
|
new_order = self.original_order_id.copy({
|
||||||
|
'purpose': self.purpose,
|
||||||
|
'note': self.note,
|
||||||
|
# Assurez-vous que 'new_quot' est défini correctement dans votre modèle
|
||||||
|
'name': self.new_quot,
|
||||||
|
})
|
||||||
|
if not self.duplicate_all_lines:
|
||||||
|
new_order.order_line.unlink()
|
||||||
|
for line_wiz in self.lines_to_duplicate.filtered('to_duplicate'):
|
||||||
|
line_wiz.sale_order_line_id.copy({'order_id': new_order.id})
|
||||||
|
|
||||||
|
# Préparation et envoi des messages de notification dans le chatter
|
||||||
|
user_name = self.env.user.name
|
||||||
|
now = fields.Datetime.now()
|
||||||
|
|
||||||
|
# Message pour la commande originale
|
||||||
|
original_msg_body = f"A new quotation <a href='#' data-oe-model='sale.order' data-oe-id='{new_order.id}'>#{new_order.name}</a> created by {user_name} duplicating this Quotation."
|
||||||
|
self.original_order_id.message_post(body=original_msg_body)
|
||||||
|
|
||||||
|
# Message pour la nouvelle commande dupliquée
|
||||||
|
new_msg_body = f"This quotation has been created by {user_name} duplicating the original Quotation <a href='#' data-oe-model='sale.order' data-oe-id='{self.original_order_id.id}'>#{self.original_order_id.name}</a>."
|
||||||
|
new_order.message_post(body=new_msg_body)
|
||||||
|
|
||||||
|
return {
|
||||||
|
'type': 'ir.actions.act_window',
|
||||||
|
'name': 'Duplicated Order',
|
||||||
|
'res_model': 'sale.order',
|
||||||
|
'res_id': new_order.id,
|
||||||
|
'view_mode': 'form',
|
||||||
|
'target': 'current',
|
||||||
|
}
|
||||||
|
|
||||||
|
@api.depends('original_order_id')
|
||||||
|
def _compute_new_quot(self):
|
||||||
|
|
||||||
|
for rec in self:
|
||||||
|
original_order_name = rec.original_order_id.name.split('-')[
|
||||||
|
0] if '-' in rec.original_order_id.name else rec.original_order_id.name
|
||||||
|
other_quotes = self.env['sale.order'].search([('name', 'like', original_order_name + '%')])
|
||||||
|
rec.new_quot = original_order_name + '-REV' + str(len(other_quotes))
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<data>
|
||||||
|
|
||||||
|
<record id="sale_order_duplication_wizard_form_view" model="ir.ui.view">
|
||||||
|
<field name="name">sale.order.duplication.wizard.form</field>
|
||||||
|
<field name="model">sale.order.duplication.wizard</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<form string="Duplicate Sale Order">
|
||||||
|
<group>
|
||||||
|
<group>
|
||||||
|
<field name="original_order_id" invisible="1"/>
|
||||||
|
<field name="new_quot" invisible="1"/>
|
||||||
|
<field name="purpose"/>
|
||||||
|
</group>
|
||||||
|
<group>
|
||||||
|
<field name="duplicate_all_lines"/>
|
||||||
|
</group>
|
||||||
|
</group>
|
||||||
|
<field name="lines_to_duplicate" attrs="{'invisible': [('duplicate_all_lines', '=', True)]}">
|
||||||
|
<tree editable="bottom">
|
||||||
|
<field name="to_duplicate"/>
|
||||||
|
<field name="sale_order_line_id" options="{'no_create': True}"/>
|
||||||
|
</tree>
|
||||||
|
</field>
|
||||||
|
<field name="note"/>
|
||||||
|
<footer>
|
||||||
|
<button string="Duplicate" type="object" name="action_duplicate_order" class="btn-primary"/>
|
||||||
|
<button string="Cancel" class="btn-default" special="cancel"/>
|
||||||
|
</footer>
|
||||||
|
</form>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="sale_order_duplication_wizard_action" model="ir.actions.act_window">
|
||||||
|
<field name="name">Duplicate Sale Order</field>
|
||||||
|
<field name="type">ir.actions.act_window</field>
|
||||||
|
<field name="res_model">sale.order.duplication.wizard</field>
|
||||||
|
<field name="view_mode">form</field>
|
||||||
|
<field name="target">new</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
41
bemade_total_show_currency/__manifest__.py
Normal file
41
bemade_total_show_currency/__manifest__.py
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
#
|
||||||
|
# Bemade Inc.
|
||||||
|
#
|
||||||
|
# Copyright (C) June 2023 Bemade Inc. (<https://www.bemade.org>).
|
||||||
|
# Author: mdurepos (Contact : it@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': 'Show Currency in total',
|
||||||
|
'version': '15.0.0.0.1',
|
||||||
|
'summary': 'Show Currency in total',
|
||||||
|
'description': """
|
||||||
|
This module adds the ability to view currency in total in all pdf.
|
||||||
|
""",
|
||||||
|
'category': 'Account',
|
||||||
|
'author': 'Bemade Inc.',
|
||||||
|
'website': 'https://www.bemade.org',
|
||||||
|
'license': 'OPL-1',
|
||||||
|
'depends': [
|
||||||
|
'sale',
|
||||||
|
'purchase',
|
||||||
|
'account'
|
||||||
|
],
|
||||||
|
'data': [
|
||||||
|
'views/total_template.xml'
|
||||||
|
],
|
||||||
|
'demo': [],
|
||||||
|
'installable': True,
|
||||||
|
'auto_install': False,
|
||||||
|
}
|
||||||
14
bemade_total_show_currency/views/total_template.xml
Normal file
14
bemade_total_show_currency/views/total_template.xml
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<odoo>
|
||||||
|
<data>
|
||||||
|
<!-- Héritage de la vue pour ajouter la devise à côté du total -->
|
||||||
|
<template id="document_tax_totals_inherit" inherit_id="account.document_tax_totals">
|
||||||
|
<xpath expr="//tr[contains(@class, 'o_total')]/td[1]/strong" position="after">
|
||||||
|
<!-- Add the currency symbol in parentheses next to "Total" -->
|
||||||
|
<t t-if="tax_totals['currency_symbol']">
|
||||||
|
<span> (<t t-esc="tax_totals['currency_symbol']"/>)</span>
|
||||||
|
</t>
|
||||||
|
</xpath>
|
||||||
|
</template>
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
1
bemade_utils/__init__.py
Normal file
1
bemade_utils/__init__.py
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
from odoo.addons.bemade_utils.tools import patch_test
|
||||||
33
bemade_utils/__manifest__.py
Normal file
33
bemade_utils/__manifest__.py
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
#
|
||||||
|
# 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': 'Bemade App Utilities',
|
||||||
|
'version': '15.0.1.0.0',
|
||||||
|
'summary': 'Utilities commonly used in Bemade modules.',
|
||||||
|
'description': 'Adds utilities such as an annotation for patching tests when modifying Odoo behaviour.',
|
||||||
|
'category': 'Technical',
|
||||||
|
'author': 'Bemade Inc.',
|
||||||
|
'website': 'http://www.bemade.org',
|
||||||
|
'license': 'OPL-1',
|
||||||
|
'depends': [],
|
||||||
|
'data': [],
|
||||||
|
'assets': {},
|
||||||
|
'installable': True,
|
||||||
|
'auto_install': False
|
||||||
|
}
|
||||||
1
bemade_utils/tests/__init__.py
Normal file
1
bemade_utils/tests/__init__.py
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
from . import test_patching_test
|
||||||
16
bemade_utils/tests/test_patching_test.py
Normal file
16
bemade_utils/tests/test_patching_test.py
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
from odoo.tests import TransactionCase
|
||||||
|
from addons.bemade_utils import patch_test
|
||||||
|
|
||||||
|
|
||||||
|
class TestA(TransactionCase):
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
super().setUpClass()
|
||||||
|
|
||||||
|
def test_method_a(self):
|
||||||
|
self.assertFalse(True)
|
||||||
|
|
||||||
|
|
||||||
|
@patch_test(TestA.test_method_a)
|
||||||
|
def test_redefining_test(self):
|
||||||
|
self.assertTrue(True)
|
||||||
2
bemade_utils/tools/__init__.py
Normal file
2
bemade_utils/tools/__init__.py
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
from .test import patch_test
|
||||||
|
|
||||||
28
bemade_utils/tools/test.py
Normal file
28
bemade_utils/tools/test.py
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
import importlib
|
||||||
|
from functools import wraps
|
||||||
|
|
||||||
|
|
||||||
|
def patch_test(original_method):
|
||||||
|
"""
|
||||||
|
A decorator that patches an Odoo test method with a new one.
|
||||||
|
The original_method is a direct reference to the method to be patched.
|
||||||
|
"""
|
||||||
|
def decorator(new_method):
|
||||||
|
@wraps(new_method)
|
||||||
|
def wrapper(*args, **kwargs):
|
||||||
|
return new_method(*args, **kwargs)
|
||||||
|
|
||||||
|
# Extract module and class names
|
||||||
|
module_name = original_method.__module__
|
||||||
|
class_name = original_method.__qualname__.split('.')[0]
|
||||||
|
|
||||||
|
# Import the module
|
||||||
|
module = importlib.import_module(module_name)
|
||||||
|
# Get the class
|
||||||
|
cls = getattr(module, class_name)
|
||||||
|
|
||||||
|
# Replace the original method with the new one
|
||||||
|
setattr(cls, original_method.__name__, wrapper)
|
||||||
|
|
||||||
|
return wrapper
|
||||||
|
return decorator
|
||||||
Loading…
Reference in a new issue