migrate from addons to new repo bemade-addons and ln in addons
This commit is contained in:
parent
751170b7f3
commit
5752f837a9
44 changed files with 849 additions and 0 deletions
5
bemade_add_follower_no_sendmail_default/__init__.py
Normal file
5
bemade_add_follower_no_sendmail_default/__init__.py
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from . import models
|
||||
|
||||
|
||||
|
||||
21
bemade_add_follower_no_sendmail_default/__manifest__.py
Normal file
21
bemade_add_follower_no_sendmail_default/__manifest__.py
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
{
|
||||
"name": "Change default when adding follower",
|
||||
"version": "15.0.0.0.1",
|
||||
"category": "Extra Tools",
|
||||
'summary': 'Change default when adding follower',
|
||||
"description": """
|
||||
Change default when adding follower
|
||||
Send mail false by default
|
||||
""",
|
||||
"author": "Bemade",
|
||||
'website': 'https://www.bemade.org',
|
||||
"depends": [
|
||||
'mail',
|
||||
],
|
||||
"data": [
|
||||
],
|
||||
"auto_install": False,
|
||||
"installable": True,
|
||||
'license': 'OPL-1'
|
||||
}
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from . import mail_wizard_invite
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
# Copyright Bemade.org
|
||||
from odoo import models, fields, api
|
||||
|
||||
|
||||
class MailWizardInviteDefault(models.TransientModel):
|
||||
_inherit = 'mail.wizard.invite'
|
||||
|
||||
send_mail = fields.Boolean(default=False, help="If true, an invitation email will be sent to the recipient")
|
||||
1
bemade_attachments_cleanup/__init__.py
Normal file
1
bemade_attachments_cleanup/__init__.py
Normal file
|
|
@ -0,0 +1 @@
|
|||
from . import wizard
|
||||
22
bemade_attachments_cleanup/__manifest__.py
Normal file
22
bemade_attachments_cleanup/__manifest__.py
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
'name': "Attachment Cleanup",
|
||||
'version': '1.0.1',
|
||||
'author': "Bemade",
|
||||
'website': "http://www.bemade.org",
|
||||
'category': 'Tools',
|
||||
'license': 'OPL-1',
|
||||
'summary': "Find and delete missing attachments",
|
||||
'description': """
|
||||
This module adds a wizard in the settings menu to find and delete missing attachments.
|
||||
The wizard lists all attachments that are not found in the filestore, and allows the user to select and delete them.
|
||||
Please use this tool with caution, as deleting attachments cannot be undone.
|
||||
""",
|
||||
'depends': ['base'],
|
||||
'data': [
|
||||
'wizard/attachment_cleanup_wizard_view.xml',
|
||||
'security/ir.model.access.csv',
|
||||
|
||||
],
|
||||
'installable': True,
|
||||
'application': False,
|
||||
}
|
||||
37
bemade_attachments_cleanup/i18n/fr_CA.po
Normal file
37
bemade_attachments_cleanup/i18n/fr_CA.po
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
# Translation of Odoo Server.
|
||||
# This file contains the translation of the following modules:
|
||||
# * attachment_cleanup
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Odoo Server 15.0\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-06-08 12:00+0000\n"
|
||||
"PO-Revision-Date: 2023-06-08 12:00+0000\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: \n"
|
||||
"Language: fr_CA\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: \n"
|
||||
|
||||
#. module: attachment_cleanup
|
||||
#: model:ir.actions.act_window,name:attachment_cleanup.action_open_attachment_cleanup_wizard
|
||||
msgid "Cleanup Attachments"
|
||||
msgstr "Nettoyer les pièces jointes"
|
||||
|
||||
#. module: attachment_cleanup
|
||||
#: model:ir.ui.view,arch_db:attachment_cleanup.view_attachment_cleanup_wizard
|
||||
msgid "Delete"
|
||||
msgstr "Supprimer"
|
||||
|
||||
#. module: attachment_cleanup
|
||||
#: model:ir.ui.view,arch_db:attachment_cleanup.view_attachment_cleanup_wizard
|
||||
msgid "Cancel"
|
||||
msgstr "Annuler"
|
||||
|
||||
#. module: attachment_cleanup
|
||||
#: model:ir.model.fields,field_description:attachment_cleanup.field_attachment_cleanup_wizard_attachment_ids
|
||||
msgid "Missing Attachments"
|
||||
msgstr "Pièces jointes manquantes"
|
||||
2
bemade_attachments_cleanup/security/ir.model.access.csv
Normal file
2
bemade_attachments_cleanup/security/ir.model.access.csv
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||
access_attachment_cleanup_wizard,access.attachment.cleanup.wizard,model_attachment_cleanup_wizard,base.group_system,1,1,1,1
|
||||
|
1
bemade_attachments_cleanup/wizard/__init__.py
Normal file
1
bemade_attachments_cleanup/wizard/__init__.py
Normal file
|
|
@ -0,0 +1 @@
|
|||
from . import attachment_cleanup_wizard
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
from odoo import api, fields, models
|
||||
import os
|
||||
|
||||
|
||||
class AttachmentCleanupWizard(models.TransientModel):
|
||||
_name = 'attachment.cleanup.wizard'
|
||||
_description = 'Attachment Cleanup Wizard'
|
||||
|
||||
attachment_ids = fields.Many2many(
|
||||
'ir.attachment',
|
||||
string='Missing Attachments',
|
||||
readonly=True,
|
||||
help='Attachments that are not found in the filestore'
|
||||
)
|
||||
|
||||
@api.model
|
||||
def default_get(self, fields_list):
|
||||
res = super(AttachmentCleanupWizard, self).default_get(fields_list)
|
||||
|
||||
attachments_1 = self.sudo().env['ir.attachment'].search([])
|
||||
|
||||
self._cr.execute("SELECT id FROM ir_attachment WHERE company_id = %s", (self.env.company.id,))
|
||||
|
||||
attachments = self._cr.fetchall()
|
||||
|
||||
filestore_location = self.env['ir.attachment']._filestore()
|
||||
|
||||
missing_attachments = []
|
||||
for attachment_id in attachments:
|
||||
# if attachment.id == 47984:
|
||||
# print(attachment.store_fname)
|
||||
attachment = self.env['ir.attachment'].browse(attachment_id)
|
||||
|
||||
if attachment.store_fname:
|
||||
file_path = os.path.join(filestore_location, attachment.store_fname)
|
||||
if not os.path.exists(file_path):
|
||||
missing_attachments.append(attachment.id)
|
||||
|
||||
if missing_attachments:
|
||||
res.update({'attachment_ids': [(6, 0, missing_attachments)]})
|
||||
return res
|
||||
|
||||
def action_cleanup_attachments(self):
|
||||
self.ensure_one()
|
||||
self.attachment_ids.unlink()
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
<odoo>
|
||||
<record id="view_attachment_cleanup_wizard" model="ir.ui.view">
|
||||
<field name="name">attachment.cleanup.wizard.view</field>
|
||||
<field name="model">attachment.cleanup.wizard</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Cleanup Attachments">
|
||||
<field name="attachment_ids" options="{'no_create': True}">
|
||||
<tree>
|
||||
<field name="name"/>
|
||||
<field name="store_fname"/>
|
||||
<field name="res_model"/>
|
||||
<field name="res_id"/>
|
||||
<field name="mimetype"/>
|
||||
<field name="write_date"/>
|
||||
<button name="unlink" type="object" string="Delete"/>
|
||||
</tree>
|
||||
</field>
|
||||
<button name="action_cleanup_attachments" type="object" string="Delete All"/>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="action_open_attachment_cleanup_wizard" model="ir.actions.act_window">
|
||||
<field name="name">Cleanup Attachments</field>
|
||||
<field name="res_model">attachment.cleanup.wizard</field>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="view_id" ref="view_attachment_cleanup_wizard"/>
|
||||
<field name="target">new</field>
|
||||
</record>
|
||||
|
||||
<menuitem id="menu_attachment_cleanup" name="Cleanup Attachments" parent="base.menu_administration" action="action_open_attachment_cleanup_wizard"/>
|
||||
</odoo>
|
||||
4
bemade_fetchmail_only_production/__init__.py
Normal file
4
bemade_fetchmail_only_production/__init__.py
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
# Copyright (C) 2023 Bemade.org
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
from . import models
|
||||
20
bemade_fetchmail_only_production/__manifest__.py
Normal file
20
bemade_fetchmail_only_production/__manifest__.py
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
{
|
||||
"name": "Fetchmail Only on production environment",
|
||||
"version": "15.0.0.0.1",
|
||||
"category": "Extra Tools",
|
||||
'summary': 'Fetchmail Only on production environment',
|
||||
"description": """
|
||||
Fetchmail Only on production environment
|
||||
""",
|
||||
"author": "Bemade",
|
||||
'website': 'https://www.bemade.org',
|
||||
"depends": [
|
||||
'fetchmail',
|
||||
],
|
||||
"data": [
|
||||
],
|
||||
"auto_install": True,
|
||||
"installable": True,
|
||||
'license': 'OPL-1'
|
||||
}
|
||||
4
bemade_fetchmail_only_production/models/__init__.py
Normal file
4
bemade_fetchmail_only_production/models/__init__.py
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
# Copyright (C) 2023 Bemade.org
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
from . import fetchmail_server
|
||||
23
bemade_fetchmail_only_production/models/fetchmail_server.py
Normal file
23
bemade_fetchmail_only_production/models/fetchmail_server.py
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
# Copyright (C) 2023 Bemade.org
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
import logging
|
||||
|
||||
# Import the required classes and decorators from Odoo
|
||||
from odoo import api, models
|
||||
from urllib.parse import urlparse
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
class fetchmail_server(models.Model):
|
||||
_inherit = 'fetchmail.server'
|
||||
|
||||
@api.model
|
||||
def fetch_mail(self):
|
||||
if urlparse(self.env['ir.config_parameter'].sudo().get_param('web.base.url')).netloc == \
|
||||
urlparse('https://erp.durpro.com/').netloc:
|
||||
return super(fetchmail_server, self).fetch_mail()
|
||||
else:
|
||||
# Add log message
|
||||
_logger.info("Trying to fetch email, current URL don't match with production URL, so we don't fetch email")
|
||||
return True
|
||||
5
bemade_hide_decimal_on_unit/__init__.py
Normal file
5
bemade_hide_decimal_on_unit/__init__.py
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# from . import models
|
||||
|
||||
|
||||
|
||||
23
bemade_hide_decimal_on_unit/__manifest__.py
Normal file
23
bemade_hide_decimal_on_unit/__manifest__.py
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
{
|
||||
"name": "Hide Decimal on unit",
|
||||
"version": "15.0.0.1.1",
|
||||
"category": "Extra Tools",
|
||||
'summary': 'Hide decimal on Qty when there is no decimal',
|
||||
"description": """
|
||||
Hide decimal on Qty when there is no decimal
|
||||
""",
|
||||
"author": "Bemade",
|
||||
'website': 'https://www.bemade.org',
|
||||
"depends": [
|
||||
'sale',
|
||||
'purchase'
|
||||
],
|
||||
"data": [
|
||||
'views/sale.xml',
|
||||
'views/purchase.xml'
|
||||
],
|
||||
"auto_install": False,
|
||||
"installable": True,
|
||||
'license': 'OPL-1'
|
||||
}
|
||||
31
bemade_hide_decimal_on_unit/views/purchase.xml
Normal file
31
bemade_hide_decimal_on_unit/views/purchase.xml
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<template id="report_purchasequotation_document_strip_decimal" inherit_id="purchase.report_purchasequotation_document">
|
||||
<xpath expr="//tbody//td[hasclass('text-right')]" position="replace">
|
||||
<td name="td_quantity" class="text-right">
|
||||
<t t-if="int(order_line.product_uom_qty) == order_line.product_uom_qty">
|
||||
<span t-esc="'%.0f' % order_line.product_uom_qty"/>
|
||||
</t>
|
||||
<t t-else="">
|
||||
<span t-esc="order_line.product_uom_qty"/>
|
||||
</t>
|
||||
<span t-field="order_line.product_uom" groups="uom.group_uom"/>
|
||||
</td>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<template id="report_purchaseorder_document_strip_decimal" inherit_id="purchase.report_purchaseorder_document">
|
||||
<xpath expr="//tbody//td[hasclass('text-right')][1]" position="replace">
|
||||
<td name="td_quantity" class="text-right">
|
||||
<t t-if="int(line.product_uom_qty) == line.product_uom_qty">
|
||||
<span t-esc="'%.0f' % line.product_uom_qty"/>
|
||||
</t>
|
||||
<t t-else="">
|
||||
<span t-esc="line.product_uom_qty"/>
|
||||
</t>
|
||||
<span t-field="line.product_uom" groups="uom.group_uom"/>
|
||||
</td>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
</odoo>
|
||||
16
bemade_hide_decimal_on_unit/views/sale.xml
Normal file
16
bemade_hide_decimal_on_unit/views/sale.xml
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<template id="report_saleorder_document_strip_decimal" inherit_id="sale.report_saleorder_document">
|
||||
<td name="td_quantity" position="replace">
|
||||
<td name="td_quantity" class="text-right">
|
||||
<t t-if="int(line.product_uom_qty) == line.product_uom_qty">
|
||||
<span t-esc="'%.0f' % line.product_uom_qty"/>
|
||||
</t>
|
||||
<t t-else="">
|
||||
<span t-esc="line.product_uom_qty"/>
|
||||
</t>
|
||||
<span t-field="line.product_uom"/>
|
||||
</td>
|
||||
</td>
|
||||
</template>
|
||||
</odoo>
|
||||
5
bemade_reordering_rules_chatter/__init__.py
Normal file
5
bemade_reordering_rules_chatter/__init__.py
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from . import models
|
||||
|
||||
|
||||
|
||||
21
bemade_reordering_rules_chatter/__manifest__.py
Normal file
21
bemade_reordering_rules_chatter/__manifest__.py
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
{
|
||||
"name": "Chatter on the Reordering Rules",
|
||||
"version": "15.0.0.0.1",
|
||||
"category": "Extra Tools",
|
||||
'license': 'GPL-3',
|
||||
'summary': 'Add chatter on the reordering rules',
|
||||
"description": """
|
||||
Add chatter on the reordering rules
|
||||
""",
|
||||
"author": "Bemade",
|
||||
'website': 'https://www.bemade.org',
|
||||
"depends": [
|
||||
'stock',
|
||||
],
|
||||
"data": [
|
||||
'views/stock_warehouse_orderpoint.xml',
|
||||
],
|
||||
"auto_install": False,
|
||||
"installable": True,
|
||||
}
|
||||
1
bemade_reordering_rules_chatter/models/__init__.py
Normal file
1
bemade_reordering_rules_chatter/models/__init__.py
Normal file
|
|
@ -0,0 +1 @@
|
|||
from . import stock_warehouse_orderpoint
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
from odoo import models, fields
|
||||
|
||||
class StockWarehouseOrderpoint(models.Model):
|
||||
_name = 'stock.warehouse.orderpoint'
|
||||
_inherit = ['stock.warehouse.orderpoint', 'mail.thread', 'mail.activity.mixin']
|
||||
|
||||
name = fields.Char(tracking=True)
|
||||
trigger = fields.Selection(tracking=True)
|
||||
active = fields.Boolean(tracking=True)
|
||||
snoozed_until = fields.Date(tracking=True)
|
||||
location_id = fields.Many2one(tracking=True)
|
||||
product_tmpl_id = fields.Many2one(tracking=True)
|
||||
product_id = fields.Many2one(tracking=True)
|
||||
product_min_qty = fields.Float(tracking=True)
|
||||
product_max_qty = fields.Float(tracking=True)
|
||||
qty_multiple = fields.Float(tracking=True)
|
||||
group_id = fields.Many2one(tracking=True)
|
||||
company_id = fields.Many2one(tracking=True)
|
||||
route_id = fields.Many2one(tracking=True)
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
|
||||
<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="model">stock.warehouse.orderpoint</field>
|
||||
<field name="inherit_id" ref="stock.view_warehouse_orderpoint_tree_editable_config"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//tree" position="attributes">
|
||||
<attribute name="editable"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_warehouse_orderpoint_form_chatter" model="ir.ui.view">
|
||||
<field name="name">stock.warehouse.orderpoint.form.chatter</field>
|
||||
<field name="model">stock.warehouse.orderpoint</field>
|
||||
<field name="inherit_id" ref="stock.view_warehouse_orderpoint_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//form/sheet" position="after">
|
||||
<div class="oe_chatter">
|
||||
<field name="message_follower_ids" widget="mail_followers" />
|
||||
<field name="message_ids" widget="mail_thread" />
|
||||
</div>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
0
bemade_so_and_po_only_company/__init__.py
Normal file
0
bemade_so_and_po_only_company/__init__.py
Normal file
22
bemade_so_and_po_only_company/__manifest__.py
Normal file
22
bemade_so_and_po_only_company/__manifest__.py
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
{
|
||||
"name": "Force Partner to be a company on SO and PO",
|
||||
"version": "15.0.0.0.1",
|
||||
"category": "Extra Tools",
|
||||
'summary': 'Force Partner to be a company on SO and PO',
|
||||
"description": """
|
||||
Force Partner to be a company on SO and PO
|
||||
""",
|
||||
"author": "Bemade",
|
||||
'website': 'https://www.bemade.org',
|
||||
"depends": [
|
||||
'stock','sale',
|
||||
],
|
||||
"data": [
|
||||
'views/sale_order.xml',
|
||||
'views/purchase_order.xml',
|
||||
],
|
||||
"auto_install": False,
|
||||
"installable": True,
|
||||
'license': 'OPL-1'
|
||||
}
|
||||
15
bemade_so_and_po_only_company/views/purchase_order.xml
Normal file
15
bemade_so_and_po_only_company/views/purchase_order.xml
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data>
|
||||
<record id="purchase_order_form_view_inherit" model="ir.ui.view">
|
||||
<field name="name">Purchase Order Form View Inherit</field>
|
||||
<field name="model">purchase.order</field>
|
||||
<field name="inherit_id" ref="purchase.purchase_order_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='partner_id']" position="attributes">
|
||||
<attribute name="domain">[('is_company', '=', True)]</attribute>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
15
bemade_so_and_po_only_company/views/sale_order.xml
Normal file
15
bemade_so_and_po_only_company/views/sale_order.xml
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data>
|
||||
<record id="sale_order_form_view_inherit" model="ir.ui.view">
|
||||
<field name="name">Sale Order Form View Inherit</field>
|
||||
<field name="model">sale.order</field>
|
||||
<field name="inherit_id" ref="sale.view_order_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='partner_id']" position="attributes">
|
||||
<attribute name="domain">[('is_company', '=', True)]</attribute>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
1
bemade_so_followers_to_picking/__init__.py
Normal file
1
bemade_so_followers_to_picking/__init__.py
Normal file
|
|
@ -0,0 +1 @@
|
|||
from . import models
|
||||
21
bemade_so_followers_to_picking/__manifest__.py
Normal file
21
bemade_so_followers_to_picking/__manifest__.py
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
{
|
||||
"name": "Add SO Followers to Picking",
|
||||
"version": "15.0.0.0.1",
|
||||
"category": "Extra Tools",
|
||||
'summary': 'Add SO Followers to Picking',
|
||||
"description": """
|
||||
Add SO Followers to Picking
|
||||
""",
|
||||
"author": "Bemade",
|
||||
'website': 'https://www.bemade.org',
|
||||
"depends": [
|
||||
'sale',
|
||||
'purchase'
|
||||
],
|
||||
"data": [
|
||||
],
|
||||
"auto_install": False,
|
||||
"installable": True,
|
||||
'license': 'OPL-1'
|
||||
}
|
||||
1
bemade_so_followers_to_picking/models/__init__.py
Normal file
1
bemade_so_followers_to_picking/models/__init__.py
Normal file
|
|
@ -0,0 +1 @@
|
|||
from . import stock_picking
|
||||
17
bemade_so_followers_to_picking/models/stock_picking.py
Normal file
17
bemade_so_followers_to_picking/models/stock_picking.py
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
from odoo import models, api
|
||||
|
||||
|
||||
class StockPicking(models.Model):
|
||||
_inherit = 'stock.picking'
|
||||
|
||||
@api.model
|
||||
def create(self, vals):
|
||||
picking = super(StockPicking, self).create(vals)
|
||||
|
||||
if 'origin' in vals:
|
||||
sale_order = self.env['sale.order'].search([('name', '=', vals['origin'])], limit=1)
|
||||
if sale_order:
|
||||
for follower in sale_order.message_follower_ids:
|
||||
picking.message_subscribe(partner_ids=[follower.partner_id.id])
|
||||
|
||||
return picking
|
||||
27
bemade_user_custom_apps_order/__init__.py
Normal file
27
bemade_user_custom_apps_order/__init__.py
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
# Copyright (C) 2023 Bemade.org
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
# Import the models package so Odoo knows to load our models
|
||||
from . import models
|
||||
|
||||
# Import the Environment for creating an environment for superuser
|
||||
# Import SUPERUSER_ID for using as the user id for the superuser
|
||||
from odoo.api import Environment, SUPERUSER_ID
|
||||
|
||||
# This is the function that gets called after the module installation
|
||||
def post_init_hook(cr, registry):
|
||||
# Create an environment with the database cursor and SUPERUSER_ID
|
||||
env = Environment(cr, SUPERUSER_ID, {})
|
||||
|
||||
menus = env['ir.ui.menu'].search([('parent_id', '=', False)])
|
||||
|
||||
# For each user in the system,
|
||||
for user in env['res.users'].search([]):
|
||||
# Iterate over each sequence
|
||||
for menu in menus:
|
||||
# Create a new record in 'res.users.menu.order' with the user, menu and sequence
|
||||
env['res.users.menu.order'].create({
|
||||
'user_id': user.id,
|
||||
'menu_id': menu.id
|
||||
})
|
||||
|
||||
42
bemade_user_custom_apps_order/__manifest__.py
Normal file
42
bemade_user_custom_apps_order/__manifest__.py
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
{
|
||||
'name': 'User Custom App Order',
|
||||
'version': '1.0.1',
|
||||
'license': 'GPL-3',
|
||||
'author': 'Bemade',
|
||||
'website': 'http://www.bemade.org',
|
||||
'category': 'Extra Tools',
|
||||
'summary': 'Allows users to customize the order of apps in the app switcher',
|
||||
'description': """
|
||||
User Custom App Order
|
||||
=====================
|
||||
This module allows each user to customize the order of their apps in the app switcher in Odoo.
|
||||
|
||||
Features:
|
||||
---------
|
||||
* Users can choose the order of their apps in the app switcher.
|
||||
* Users can only modify their own app order. Administrators can modify the order for any user.
|
||||
* The module also automatically handles the creation of app order records for new users and new apps.
|
||||
|
||||
Installation:
|
||||
-------------
|
||||
No specific steps required for the installation.
|
||||
|
||||
Configuration:
|
||||
--------------
|
||||
No specific configuration required for this module.
|
||||
""",
|
||||
'depends': ['hr',],
|
||||
'data': [
|
||||
'security/ir.model.access.csv',
|
||||
'views/user_menu_sequence.xml',
|
||||
'views/res_users_view.xml'
|
||||
],
|
||||
'assets': {
|
||||
'web.assets_tests': ['bemade_user_custom_apps_order/static/tests/tours/custom_app_order_tour.js'],
|
||||
},
|
||||
'installable': True,
|
||||
'application': False,
|
||||
'auto_install': False,
|
||||
'post_init_hook': 'post_init_hook',
|
||||
}
|
||||
9
bemade_user_custom_apps_order/models/__init__.py
Normal file
9
bemade_user_custom_apps_order/models/__init__.py
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
# Copyright (C) 2023 Bemade.org
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
# Import the models to extend
|
||||
from . import res_users
|
||||
from . import ir_ui_menu
|
||||
|
||||
# Import the new model
|
||||
from . import res_users_menu_order
|
||||
84
bemade_user_custom_apps_order/models/ir_ui_menu.py
Normal file
84
bemade_user_custom_apps_order/models/ir_ui_menu.py
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
# Copyright (C) 2023 Bemade.org
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
# Import the required classes and decorators from Odoo
|
||||
from odoo import api, models
|
||||
|
||||
class IrUiMenu(models.Model):
|
||||
_inherit = 'ir.ui.menu'
|
||||
|
||||
@api.model_create_multi
|
||||
def create(self, vals_list):
|
||||
# Override the create method to handle new top-level menus
|
||||
records = super(IrUiMenu, self).create(vals_list)
|
||||
|
||||
menus = self.env['ir.ui.menu'].search([('parent_id', '=', False)])
|
||||
|
||||
# For each new menu
|
||||
for record in records:
|
||||
# If the menu is a top-level menu
|
||||
if not record.parent_id and record.id in menus.ids:
|
||||
# Create a 'res.users.menu.order' record for each user
|
||||
for user in self.env['res.users'].search([]):
|
||||
self.env['res.users.menu.order'].create({
|
||||
'user_id': user.id,
|
||||
'menu_id': record.id,
|
||||
'sequence': record.sequence,
|
||||
})
|
||||
return records
|
||||
|
||||
def unlink(self):
|
||||
# Override the unlink method to delete associated 'res.users.menu.order' records
|
||||
self.env['res.users.menu.order'].search([
|
||||
('menu_id', 'in', self.ids)
|
||||
]).unlink()
|
||||
return super(IrUiMenu, self).unlink()
|
||||
|
||||
def write(self, vals):
|
||||
# Call the super method to perform the default write operation
|
||||
res = super(IrUiMenu, self).write(vals)
|
||||
|
||||
menus = self.env['ir.ui.menu'].search([('parent_id', '=', False)])
|
||||
|
||||
|
||||
for self_record in self:
|
||||
if 'parent_id' in vals:
|
||||
if vals.get('parent_id') == False and self_record in menus:
|
||||
# If the menu becomes a top-level menu, create a 'res.users.menu.order' record for each user
|
||||
users = self.env['res.users'].search([])
|
||||
for user in users:
|
||||
existing_order = self.env['res.users.menu.order'].search([
|
||||
('user_id', '=', user.id),
|
||||
('menu_id', '=', self_record.id),
|
||||
], limit=1)
|
||||
if not existing_order:
|
||||
self.env['res.users.menu.order'].create({
|
||||
'user_id': user.id,
|
||||
'menu_id': self_record.id,
|
||||
'sequence': self_record.sequence,
|
||||
})
|
||||
else:
|
||||
# Delete the corresponding 'res.users.menu.order' records
|
||||
self.env['res.users.menu.order'].search([('menu_id', '=', self_record.id)]).unlink()
|
||||
return res
|
||||
|
||||
@api.model
|
||||
def load_menus(self, debug):
|
||||
menus = super().load_menus(debug)
|
||||
|
||||
# Retrieve the user's menu order records
|
||||
menu_order_records = self.env['res.users.menu.order'].search([('user_id', '=', self.env.uid)])
|
||||
|
||||
# Create a dictionary that maps app ids to sequences
|
||||
sequence_dict = {record.menu_id.id: record.sequence for record in menu_order_records}
|
||||
|
||||
# Retrieve the menu records for the IDs in menus['root']['children']
|
||||
menu_records = self.browse(menus['root']['children'])
|
||||
|
||||
# Sort the menu records based on the sequences in the dictionary
|
||||
sorted_menu_records = menu_records.sorted(key=lambda menu: sequence_dict.get(menu.id, 9999))
|
||||
|
||||
# Replace menus['root']['children'] with the IDs of the sorted menu records
|
||||
menus['root']['children'] = sorted_menu_records.ids
|
||||
|
||||
return menus
|
||||
63
bemade_user_custom_apps_order/models/res_users.py
Normal file
63
bemade_user_custom_apps_order/models/res_users.py
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
# Copyright (C) 2023 Bemade.org
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
from odoo import models, api, fields
|
||||
|
||||
class ResUsers(models.Model):
|
||||
_inherit = 'res.users'
|
||||
|
||||
# Define the One2many field for the app order records
|
||||
app_order_ids = fields.One2many(
|
||||
'res.users.menu.order', 'user_id', string='App Order Records')
|
||||
|
||||
@property
|
||||
def SELF_READABLE_FIELDS(self):
|
||||
return super().SELF_READABLE_FIELDS + ['app_order_ids']
|
||||
|
||||
@property
|
||||
def SELF_WRITEABLE_FIELDS(self):
|
||||
return super().SELF_WRITEABLE_FIELDS + ['app_order_ids']
|
||||
|
||||
@api.model
|
||||
def create(self, vals):
|
||||
"""
|
||||
Overridden to create a new app order record for each top-level menu for the new user when a new user is created.
|
||||
"""
|
||||
# Create the user
|
||||
new_user = super().create(vals)
|
||||
|
||||
# Create a new app order record for each top-level menu for the new user
|
||||
menus = self.env['ir.ui.menu'].search([('parent_id', '=', False)])
|
||||
for menu in menus:
|
||||
self.env['res.users.menu.order'].create({
|
||||
'user_id': new_user.id,
|
||||
'menu_id': menu.id,
|
||||
'sequence': 9999, # Or some other default sequence
|
||||
})
|
||||
|
||||
return new_user
|
||||
|
||||
def unlink(self):
|
||||
"""
|
||||
Overridden to delete the app order records for the user being deleted when a user is deleted.
|
||||
"""
|
||||
# Delete the app order records for the user being deleted
|
||||
self.env['res.users.menu.order'].search([('user_id', 'in', self.ids)]).unlink()
|
||||
|
||||
# Delete the user
|
||||
return super().unlink()
|
||||
|
||||
def write(self, vals):
|
||||
"""
|
||||
Overridden to allow users to modify their own 'app_order_ids' field.
|
||||
"""
|
||||
if 'app_order_ids' in vals and self.env.uid == self.id:
|
||||
# may be called with sudo can be replaced with WRITINGFIELDRIGHTS but works for now
|
||||
super(ResUsers, self.sudo()).write(vals)
|
||||
else:
|
||||
super().write(vals)
|
||||
|
||||
return {
|
||||
'type': 'ir.actions.client',
|
||||
'tag': 'reload',
|
||||
}
|
||||
28
bemade_user_custom_apps_order/models/res_users_menu_order.py
Normal file
28
bemade_user_custom_apps_order/models/res_users_menu_order.py
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
# Copyright (C) 2023 Bemade.org
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
# Import the required classes and decorators from Odoo
|
||||
from odoo import models, fields, api
|
||||
|
||||
class ResUsersMenuOrder(models.Model):
|
||||
"""
|
||||
This model represents the preferred order in which a user wants to see their apps.
|
||||
Each record represents the order of one app for one user.
|
||||
"""
|
||||
|
||||
_name = 'res.users.menu.order'
|
||||
_description = 'User Menu Order'
|
||||
|
||||
user_id = fields.Many2one('res.users', string='User', required=True, )
|
||||
# The user who has set a preferred order for their apps.
|
||||
|
||||
menu_id = fields.Many2one('ir.ui.menu', string='Menu', required=True)
|
||||
# The app that the user has set a preferred order for.
|
||||
|
||||
sequence = fields.Integer(string='Sequence')
|
||||
# The order of the app. A lower sequence means the app will appear earlier.
|
||||
|
||||
_sql_constraints = [
|
||||
('user_menu_uniq', 'unique(user_id, menu_id)', 'A user should only have one order record per menu!'),
|
||||
]
|
||||
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||
access_res_users_menu_order_all,res.users.menu.order.all,model_res_users_menu_order,base.group_user,1,1,1,1
|
||||
access_res_users_menu_order_admin,res.users.menu.order.admin,model_res_users_menu_order,base.group_system,1,1,1,1
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
/** @odoo-module */
|
||||
|
||||
import tour from 'web_tour.tour';
|
||||
|
||||
tour.register('custom_app_order_tour', {
|
||||
test: true,
|
||||
url: '/web',
|
||||
}, /* Make sure the test task is visible via the Project > Task Templates path */
|
||||
[
|
||||
{
|
||||
trigger: 'div.o_user_menu button.dropdown-toggle',
|
||||
},
|
||||
{
|
||||
trigger: 'span[data-menu="settings"]:contains(My Profile)'
|
||||
},
|
||||
{
|
||||
trigger: 'a.nav-link:contains(App Order)'
|
||||
}
|
||||
])
|
||||
1
bemade_user_custom_apps_order/tests/__init__.py
Normal file
1
bemade_user_custom_apps_order/tests/__init__.py
Normal file
|
|
@ -0,0 +1 @@
|
|||
from . import test_user_custom_app_order
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
from odoo.tests import TransactionCase, HttpCase, tagged
|
||||
from odoo import Command
|
||||
|
||||
|
||||
class TestCustomAppOrder(TransactionCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
user_group = cls.env.ref('base.group_user')
|
||||
|
||||
Users = cls.env['res.users'].with_context({'no_reset_password': True})
|
||||
Employees = cls.env['hr.employee']
|
||||
|
||||
user_partner = cls.env['res.partner'].create({
|
||||
'name': 'User Partner',
|
||||
})
|
||||
cls.user = Users.create({
|
||||
'name': 'Regular User',
|
||||
'login': 'reguser',
|
||||
'partner_id': user_partner.id,
|
||||
'password': 'reguser',
|
||||
'email': 'regular_user@test.co',
|
||||
'groups_id': [(6, 0, [user_group.id])]
|
||||
})
|
||||
user_employee = Employees.create({
|
||||
'name': 'Regular User',
|
||||
'address_home_id': user_partner.id,
|
||||
'user_id': cls.user.id,
|
||||
})
|
||||
|
||||
|
||||
@tagged('-at_install', 'post_install')
|
||||
class TestCustomAppOrderTours(HttpCase, TestCustomAppOrder):
|
||||
|
||||
def test_as_regular_user(self):
|
||||
self.start_tour('/web', 'custom_app_order_tour', login='reguser')
|
||||
33
bemade_user_custom_apps_order/views/res_users_view.xml
Normal file
33
bemade_user_custom_apps_order/views/res_users_view.xml
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
|
||||
<!-- Extend the base view for the user profile -->
|
||||
<record id="view_users_form_profile" model="ir.ui.view">
|
||||
<field name="name">res.users.preferences.form.inherit</field>
|
||||
<field name="model">res.users</field>
|
||||
<field name="inherit_id" ref="base.view_users_form_simple_modif"/>
|
||||
<field name="arch" type="xml">
|
||||
|
||||
<!-- Add a new page to the notebook on the user profile -->
|
||||
<xpath expr="//notebook" position="inside">
|
||||
<page string="App Order">
|
||||
|
||||
<!-- Add a list view for the 'app_order_ids' field -->
|
||||
<field name="app_order_ids" widget="one2many_list" create="false" delete="false">
|
||||
|
||||
<!-- Use the 'handle' widget for the sequence field -->
|
||||
<!-- This gives a draggable handle in the list view -->
|
||||
<!-- The sequence will be updated based on the new order -->
|
||||
<!-- Set 'readonly' to False for the sequence field so it can be modified -->
|
||||
<tree editable="bottom" limit="200">
|
||||
<field name="sequence" widget="handle"/>
|
||||
<field name="menu_id" readonly="1"/>
|
||||
<field name="user_id" invisible="1"/>
|
||||
</tree>
|
||||
</field>
|
||||
</page>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
35
bemade_user_custom_apps_order/views/user_menu_sequence.xml
Normal file
35
bemade_user_custom_apps_order/views/user_menu_sequence.xml
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
|
||||
<!-- Record to specify the res.users.menu.order form view -->
|
||||
<record id="user_menu_sequence_form" model="ir.ui.view">
|
||||
<field name="name">user.menu.sequence.form</field>
|
||||
<field name="model">res.users.menu.order</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="User Menu Order">
|
||||
<!-- Include the fields user_id, menu_id and sequence -->
|
||||
<group>
|
||||
<field name="user_id"/>
|
||||
<field name="menu_id"/>
|
||||
<field name="sequence"/>
|
||||
</group>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- Record to specify the res.users.menu.order tree view -->
|
||||
<record id="user_menu_sequence_tree" model="ir.ui.view">
|
||||
<field name="name">user.menu.sequence.tree</field>
|
||||
<field name="model">res.users.menu.order</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="User Menu Order">
|
||||
<!-- Include the fields user_id, menu_id and sequence -->
|
||||
<!-- Add a handle widget to the sequence field for easy reordering -->
|
||||
<field name="sequence" widget="handle"/>
|
||||
<field name="user_id"/>
|
||||
<field name="menu_id"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
Loading…
Reference in a new issue