Compare commits

...

10 commits

65 changed files with 10007 additions and 2225 deletions

View file

@ -43,6 +43,9 @@ Changelog
- 16.0.1.1.0: Add Queue Job support and move to server action - 16.0.1.1.0: Add Queue Job support and move to server action
- 16.0.1.1.1: Use default image as product image - 16.0.1.1.1: Use default image as product image
- 16.0.1.2.0: Add Extra Product Media support - 16.0.1.2.0: Add Extra Product Media support
- 16.0.1.2.1: Add image mime type check
- 16.0.1.2.3: Increase the max image pixels limit for PIL to avoid DecompressionBombError
- 16.0.1.3.0: Add Cathegories, diet and organisation fields
Bug Tracker Bug Tracker
=========== ===========

View file

@ -4,10 +4,13 @@
"author": "BAKEUP", "author": "BAKEUP",
"website": "https://www.bakeup.org", "website": "https://www.bakeup.org",
"category": "product", "category": "product",
"version": "16.0.1.2.0", "version": "16.0.1.3.0",
"depends": ["product", "queue_job", "website_sale"], "depends": ["product", "queue_job", "website_sale"],
"data": [ "data": [
"security/ir.model.access.csv", "security/ir.model.access.csv",
"data/datanature_warengruppen.xml",
"data/product_attribute_verband.xml",
"data/product_attribute_ernaehrungshinweise.xml",
"views/product_template_views.xml", "views/product_template_views.xml",
"views/res_config_settings_views.xml", "views/res_config_settings_views.xml",
], ],

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,40 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="attr_ernaehrungshinweise" model="product.attribute">
<field name="name">Ernährungshinweise</field>
<field name="create_variant">no_variant</field>
</record>
<record id="attr_ernaehrungshinweise_val_vegan" model="product.attribute.value">
<field name="attribute_id" ref="attr_ernaehrungshinweise"/>
<field name="name">Vegan</field>
</record>
<record id="attr_ernaehrungshinweise_val_vegetarisch" model="product.attribute.value">
<field name="attribute_id" ref="attr_ernaehrungshinweise"/>
<field name="name">Vegetarisch</field>
</record>
<record id="attr_ernaehrungshinweise_val_laktosefrei" model="product.attribute.value">
<field name="attribute_id" ref="attr_ernaehrungshinweise"/>
<field name="name">Laktosefrei</field>
</record>
<record id="attr_ernaehrungshinweise_val_glutenfrei" model="product.attribute.value">
<field name="attribute_id" ref="attr_ernaehrungshinweise"/>
<field name="name">Glutenfrei</field>
</record>
<record id="attr_ernaehrungshinweise_val_rohkost" model="product.attribute.value">
<field name="attribute_id" ref="attr_ernaehrungshinweise"/>
<field name="name">Rohkost</field>
</record>
<record id="attr_ernaehrungshinweise_val_ohne_schweinefleisch" model="product.attribute.value">
<field name="attribute_id" ref="attr_ernaehrungshinweise"/>
<field name="name">Ohne Schweinefleisch</field>
</record>
<record id="attr_ernaehrungshinweise_val_zuckerfrei" model="product.attribute.value">
<field name="attribute_id" ref="attr_ernaehrungshinweise"/>
<field name="name">Zuckerfrei</field>
</record>
<record id="attr_ernaehrungshinweise_val_ohne_zuckerzusatz" model="product.attribute.value">
<field name="attribute_id" ref="attr_ernaehrungshinweise"/>
<field name="name">Ohne Zuckerzusatz</field>
</record>
</odoo>

View file

@ -0,0 +1,104 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="attr_verband" model="product.attribute">
<field name="name">Verband</field>
<field name="create_variant">no_variant</field>
</record>
<record id="attr_verband_val_1" model="product.attribute.value">
<field name="attribute_id" ref="attr_verband"/>
<field name="name">Naturland (DN)</field>
</record>
<record id="attr_verband_val_2" model="product.attribute.value">
<field name="attribute_id" ref="attr_verband"/>
<field name="name">Bioland (DB)</field>
</record>
<record id="attr_verband_val_3" model="product.attribute.value">
<field name="attribute_id" ref="attr_verband"/>
<field name="name">Ecoland (DC)</field>
</record>
<record id="attr_verband_val_4" model="product.attribute.value">
<field name="attribute_id" ref="attr_verband"/>
<field name="name">Gäa (DG)</field>
</record>
<record id="attr_verband_val_5" model="product.attribute.value">
<field name="attribute_id" ref="attr_verband"/>
<field name="name">Biokreis (DK)</field>
</record>
<record id="attr_verband_val_6" model="product.attribute.value">
<field name="attribute_id" ref="attr_verband"/>
<field name="name">Verbund Ökohöfe (DV)</field>
</record>
<record id="attr_verband_val_7" model="product.attribute.value">
<field name="attribute_id" ref="attr_verband"/>
<field name="name">Ecovin (DW)</field>
</record>
<record id="attr_verband_val_8" model="product.attribute.value">
<field name="attribute_id" ref="attr_verband"/>
<field name="name">Bio Suisse (BS)</field>
</record>
<record id="attr_verband_val_9" model="product.attribute.value">
<field name="attribute_id" ref="attr_verband"/>
<field name="name">Biopark (DP)</field>
</record>
<record id="attr_verband_val_10" model="product.attribute.value">
<field name="attribute_id" ref="attr_verband"/>
<field name="name">IFOAM (IA)</field>
</record>
<record id="attr_verband_val_11" model="product.attribute.value">
<field name="attribute_id" ref="attr_verband"/>
<field name="name">Naturland (in Umstellung) (UW)</field>
</record>
<record id="attr_verband_val_12" model="product.attribute.value">
<field name="attribute_id" ref="attr_verband"/>
<field name="name">Bioland (in Umstellung) (UW)</field>
</record>
<record id="attr_verband_val_13" model="product.attribute.value">
<field name="attribute_id" ref="attr_verband"/>
<field name="name">Ecoland (in Umstellung) (UW)</field>
</record>
<record id="attr_verband_val_14" model="product.attribute.value">
<field name="attribute_id" ref="attr_verband"/>
<field name="name">Gäa (in Umstellung) (UW)</field>
</record>
<record id="attr_verband_val_15" model="product.attribute.value">
<field name="attribute_id" ref="attr_verband"/>
<field name="name">Biokreis (in Umstellung) (UW)</field>
</record>
<record id="attr_verband_val_16" model="product.attribute.value">
<field name="attribute_id" ref="attr_verband"/>
<field name="name">Verbund Ökohöfe (in Umstellung) (UW)</field>
</record>
<record id="attr_verband_val_17" model="product.attribute.value">
<field name="attribute_id" ref="attr_verband"/>
<field name="name">Ecovin (in Umstellung) (UW)</field>
</record>
<record id="attr_verband_val_18" model="product.attribute.value">
<field name="attribute_id" ref="attr_verband"/>
<field name="name">Bio Suisse (in Umstellung) (UW)</field>
</record>
<record id="attr_verband_val_19" model="product.attribute.value">
<field name="attribute_id" ref="attr_verband"/>
<field name="name">Bio Austria (in Umstellung) (UW)</field>
</record>
<record id="attr_verband_val_20" model="product.attribute.value">
<field name="attribute_id" ref="attr_verband"/>
<field name="name">Biopark (in Umstellung) (UW)</field>
</record>
<record id="attr_verband_val_21" model="product.attribute.value">
<field name="attribute_id" ref="attr_verband"/>
<field name="name">Bio Austria (BA)</field>
</record>
<record id="attr_verband_val_22" model="product.attribute.value">
<field name="attribute_id" ref="attr_verband"/>
<field name="name">Demeter (DD)</field>
</record>
<record id="attr_verband_val_23" model="product.attribute.value">
<field name="attribute_id" ref="attr_verband"/>
<field name="name">Demeter (in Umstellung) (UW)</field>
</record>
<record id="attr_verband_val_24" model="product.attribute.value">
<field name="attribute_id" ref="attr_verband"/>
<field name="name">Naturland Fair (NF)</field>
</record>
</odoo>

File diff suppressed because it is too large Load diff

View file

@ -1,2 +1,3 @@
from . import product_public_category
from . import product_template from . import product_template
from . import res_config_settings from . import res_config_settings

View file

@ -0,0 +1,11 @@
from odoo import fields, models
class ProductPublicCategory(models.Model):
_inherit = "product.public.category"
datanature_warengruppe_id = fields.Char(
string="DataNature Warengruppe ID",
index=True,
copy=False,
)

View file

@ -1,14 +1,62 @@
from odoo import models from PIL import Image
from odoo import fields, models
from .. import utils as dn_utils from .. import utils as dn_utils
# Increase the max image pixels limit for PIL to avoid DecompressionBombError
Image.MAX_IMAGE_PIXELS = 2 * 189135000 # set custom limit for large images
ERNAEHRUNGSHINWEISE_FIELDS = [
("ernhinweis_vegan", "data_nature_importer.attr_ernaehrungshinweise_val_vegan"),
(
"ernhinweis_vegetarisch",
"data_nature_importer.attr_ernaehrungshinweise_val_vegetarisch",
),
(
"ernhinweis_laktosefrei",
"data_nature_importer.attr_ernaehrungshinweise_val_laktosefrei",
),
(
"ernhinweis_glutenfrei",
"data_nature_importer.attr_ernaehrungshinweise_val_glutenfrei",
),
("ernhinweis_rohkost", "data_nature_importer.attr_ernaehrungshinweise_val_rohkost"),
(
"ernhinweis_ohne_schweinefleisch",
"data_nature_importer.attr_ernaehrungshinweise_val_ohne_schweinefleisch",
),
(
"ernhinweis_zuckerfrei",
"data_nature_importer.attr_ernaehrungshinweise_val_zuckerfrei",
),
(
"ernhinweis_ohne_zuckerzusatz",
"data_nature_importer.attr_ernaehrungshinweise_val_ohne_zuckerzusatz",
),
]
class ProductTemplate(models.Model): class ProductTemplate(models.Model):
_inherit = "product.template" _inherit = "product.template"
def action_sync_datanature_image(self): producer_id = fields.Many2one(
for product in self: "res.partner",
product.with_delay()._sync_dn_images() string="Producer",
domain=[("is_company", "=", True)],
index=True,
)
producer_name = fields.Char(
related="producer_id.name", store=True, string="Producer Name"
)
def action_sync_datanature_information(self):
if len(self) == 1:
self._sync_dn_information()
else:
for product in self:
product.with_delay()._sync_dn_information()
def _get_dn_metadata(self): def _get_dn_metadata(self):
self.ensure_one() self.ensure_one()
@ -19,17 +67,103 @@ class ProductTemplate(models.Model):
return False return False
return metadata[0] return metadata[0]
def _sync_dn_images(self): def _sync_dn_information(self):
self.ensure_one() self.ensure_one()
metadata = self._get_dn_metadata() metadata = self._get_dn_metadata()
if not metadata or not metadata.get("images"): if not metadata:
return False return False
self._sync_dn_category(metadata)
self._sync_dn_images(metadata)
self._sync_dn_verband(metadata)
self._sync_dn_ernaehrungshinweise(metadata)
self._sync_dn_producer(metadata)
return True
def _sync_dn_category(self, metadata):
warengruppe_id = metadata.get("warengruppe_id")
if not warengruppe_id:
return
category = self.env["product.public.category"].search(
[("datanature_warengruppe_id", "=", warengruppe_id)], limit=1
)
if category and category not in self.public_categ_ids:
self.public_categ_ids = [(4, category.id)]
def _sync_dn_verband(self, metadata):
verband_id = metadata.get("bio_verband_id")
if not verband_id:
return
attr_value = self.env.ref(
"data_nature_importer.attr_verband_val_%s" % verband_id,
raise_if_not_found=False,
)
if not attr_value:
return
attribute = self.env.ref("data_nature_importer.attr_verband")
attr_line = self.attribute_line_ids.filtered(
lambda l: l.attribute_id == attribute
)
if attr_line:
if attr_line.value_ids != attr_value:
attr_line.value_ids = [(6, 0, [attr_value.id])]
else:
self.env["product.template.attribute.line"].create(
{
"product_tmpl_id": self.id,
"attribute_id": attribute.id,
"value_ids": [(6, 0, [attr_value.id])],
}
)
def _sync_dn_ernaehrungshinweise(self, metadata):
active_ids = [
self.env.ref(xmlid).id
for field, xmlid in ERNAEHRUNGSHINWEISE_FIELDS
if metadata.get(field)
]
attribute = self.env.ref("data_nature_importer.attr_ernaehrungshinweise")
attr_line = self.attribute_line_ids.filtered(
lambda l: l.attribute_id == attribute
)
if not active_ids:
attr_line.unlink()
return
if attr_line:
if set(attr_line.value_ids.ids) != set(active_ids):
attr_line.value_ids = [(6, 0, active_ids)]
else:
self.env["product.template.attribute.line"].create(
{
"product_tmpl_id": self.id,
"attribute_id": attribute.id,
"value_ids": [(6, 0, active_ids)],
}
)
def _sync_dn_producer(self, metadata):
name = metadata.get("pbm_markenname")
if not name:
return
partner = self.env["res.partner"].search(
[("name", "=", name), ("is_company", "=", True)], limit=1
)
if not partner:
partner = self.env["res.partner"].create({"name": name, "is_company": True})
if self.producer_id != partner:
self.producer_id = partner
# TODO: remove legacy methode aber cleaning up failed jobs
def _sync_datanature_image(self, metadata):
return self._sync_dn_images(metadata)
def _sync_dn_images(self, metadata):
images = metadata.get("images") images = metadata.get("images")
if not images: if not images:
return False return False
image_id = None
for image in images: for image in images:
image_id = image.get("id") image_id = image.get("id")
if image.get("mime_type") not in ["image/jpeg", "image/png"]:
continue
image_data = dn_utils.get_datanature_image(self, image_id, token=None) image_data = dn_utils.get_datanature_image(self, image_id, token=None)
if not image_data: if not image_data:
continue continue
@ -37,14 +171,11 @@ class ProductTemplate(models.Model):
self.image_1920 = image_data self.image_1920 = image_data
else: else:
ProductImage = self.env["product.image"] ProductImage = self.env["product.image"]
if ( if ProductImage.search_count(
ProductImage.search_count( [
[ ("product_tmpl_id", "=", self.id),
("product_tmpl_id", "=", self.id), ("name", "=", f"DATA Nature Image: {image_id}"),
("name", "=", f"DATA Nature Image: {image_id}"), ]
]
)
> 0
): ):
continue continue
ProductImage.create( ProductImage.create(

View file

@ -3,3 +3,6 @@
- 16.0.1.1.0: Add Queue Job support and move to server action - 16.0.1.1.0: Add Queue Job support and move to server action
- 16.0.1.1.1: Use default image as product image - 16.0.1.1.1: Use default image as product image
- 16.0.1.2.0: Add Extra Product Media support - 16.0.1.2.0: Add Extra Product Media support
- 16.0.1.2.1: Add image mime type check
- 16.0.1.2.3: Increase the max image pixels limit for PIL to avoid DecompressionBombError
- 16.0.1.3.0: Add Cathegories, diet and organisation fields

View file

@ -386,6 +386,9 @@ ul.auto-toc {
<li>16.0.1.1.0: Add Queue Job support and move to server action</li> <li>16.0.1.1.0: Add Queue Job support and move to server action</li>
<li>16.0.1.1.1: Use default image as product image</li> <li>16.0.1.1.1: Use default image as product image</li>
<li>16.0.1.2.0: Add Extra Product Media support</li> <li>16.0.1.2.0: Add Extra Product Media support</li>
<li>16.0.1.2.1: Add image mime type check</li>
<li>16.0.1.2.3: Increase the max image pixels limit for PIL to avoid DecompressionBombError</li>
<li>16.0.1.3.0: Add Cathegories, diet and organisation fields</li>
</ul> </ul>
</div> </div>
<div class="section" id="bug-tracker"> <div class="section" id="bug-tracker">

View file

@ -70,6 +70,53 @@ def _parse_products(xml_bytes):
"zutatenlegende_txt": prod.findtext( "zutatenlegende_txt": prod.findtext(
"dn:zut_zutatenlegende_txt", default="", namespaces=ns "dn:zut_zutatenlegende_txt", default="", namespaces=ns
), ),
"warengruppe_id": prod.findtext(
"dn:pbm_warengruppe_id", default="", namespaces=ns
),
"warengruppe_kuerzel": prod.findtext(
"dn:pbm_warengruppe_kuerzel", default="", namespaces=ns
),
"warengruppe_pfad": prod.findtext(
"dn:pbm_warengruppe_pfad", default="", namespaces=ns
),
"bio_verband_id": prod.findtext(
"dn:bio_verband_id", default="", namespaces=ns
),
"pbm_markenname": prod.findtext(
"dn:pbm_markenname", default="", namespaces=ns
),
"ernhinweis_vegan": prod.findtext(
"dn:ernhinweis_vegan", default="", namespaces=ns
)
== "true",
"ernhinweis_vegetarisch": prod.findtext(
"dn:ernhinweis_vegetarisch", default="", namespaces=ns
)
== "true",
"ernhinweis_laktosefrei": prod.findtext(
"dn:ernhinweis_laktosefrei", default="", namespaces=ns
)
== "true",
"ernhinweis_glutenfrei": prod.findtext(
"dn:ernhinweis_glutenfrei", default="", namespaces=ns
)
== "true",
"ernhinweis_rohkost": prod.findtext(
"dn:ernhinweis_rohkost", default="", namespaces=ns
)
== "true",
"ernhinweis_ohne_schweinefleisch": prod.findtext(
"dn:ernhinweis_ohne_schweinefleisch", default="", namespaces=ns
)
== "true",
"ernhinweis_zuckerfrei": prod.findtext(
"dn:ernhinweis_zuckerfrei", default="", namespaces=ns
)
== "true",
"ernhinweis_ohne_zuckerzusatz": prod.findtext(
"dn:ernhinweis_ohne_zuckerzusatz", default="", namespaces=ns
)
== "true",
"beschreibung_kurz": prod.findtext( "beschreibung_kurz": prod.findtext(
"dn:markinf_produktbeschreibung_kurz", default="", namespaces=ns "dn:markinf_produktbeschreibung_kurz", default="", namespaces=ns
), ),

View file

@ -1,13 +1,22 @@
<odoo> <odoo>
<record id="model_product_template_action_sync_image" model="ir.actions.server"> <record id="product_template_form_view_datanature" model="ir.ui.view">
<field name="name">Sync Datanature Image</field> <field name="name">product.template.form.datanature</field>
<field name="model_id" ref="product.model_product_template"/> <field <field name="model">product.template</field>
name="binding_model_id" <field name="inherit_id" ref="product.product_template_form_view"/>
ref="product.model_product_template" <field name="arch" type="xml">
/> <xpath expr="//page[@name='general_information']//group[@name='group_general']" position="inside">
<field name="producer_id"/>
</xpath>
</field>
</record>
<record id="model_product_template_action_sync_information" model="ir.actions.server">
<field name="name">Sync DataNature Information</field>
<field name="model_id" ref="product.model_product_template"/>
<field name="binding_model_id" ref="product.model_product_template"/>
<field name="state">code</field> <field name="state">code</field>
<field name="code"> <field name="code">
action = records.action_sync_datanature_image() action = records.action_sync_datanature_information()
</field> </field>
</record> </record>
</odoo> </odoo>

View file

@ -42,19 +42,22 @@ In webshop checkout it adds
Changelog Changelog
========= =========
:0.0.1: Initial module. - 16.0.0.0.1: Initial module.
:1.0.0: Live - 16.0.1.0.0: Live
:1.1.0: Add Open Packagings Filter and fix rounding issue - 16.0.1.1.0: Add Open Packagings Filter and fix rounding issue
:1.1.1: Pass arguments to super again - 16.0.1.1.1: Pass arguments to super again
:1.1.2: Make publish on website multi company - 16.0.1.1.2: Make publish on website multi company
:1.2.0: Add Open Packages to Product list - 16.0.1.2.0: Add Open Packages to Product list
:1.3.0: Adapt new Open Packages format and add indicators - 16.0.1.3.0: Adapt new Open Packages format and add indicators
:1.3.1: Remove Max Qty from first page - 16.0.1.3.1: Remove Max Qty from first page
:1.3.2: Add decimal to ribbon - 16.0.1.3.2: Add decimal to ribbon
:1.4.0: Add credit warning and avoid debts option - 16.0.1.4.0: Add credit warning and avoid debts option
:1.4.1: Add credit to portal details - 16.0.1.4.1: Add credit to portal details
:1.4.2: Catch unescaped float values - 16.0.1.4.2: Catch unescaped float values
:1.4.3: Drop session if SO belongs to another website (Odoo 16 bug) - 16.0.1.4.3: Drop session if SO belongs to another website (Odoo 16 bug)
- 16.0.1.4.3: Remove is_published filtering and use job manager only on more than one
- 16.0.1.4.5: Make open Packagings more visible
- 16.0.1.5.0: Make attribute selection an AND-filter
Bug Tracker Bug Tracker
=========== ===========

View file

@ -4,7 +4,7 @@
"author": "BAKEUP", "author": "BAKEUP",
"website": "https://www.bakeup.org", "website": "https://www.bakeup.org",
"category": "website", "category": "website",
"version": "16.0.1.4.3", "version": "16.0.1.5.0",
"depends": [ "depends": [
"sale_product_configurator", "sale_product_configurator",
"website_sale", "website_sale",

View file

@ -1,5 +1,6 @@
from odoo import fields, http from odoo import fields, http
from odoo.http import request from odoo.http import request
from odoo.osv import expression
from odoo.tools import lazy from odoo.tools import lazy
from odoo.addons.website_sale.controllers.main import WebsiteSale from odoo.addons.website_sale.controllers.main import WebsiteSale
@ -73,6 +74,28 @@ class WebsiteSale(WebsiteSale):
new_values["packaging_info"] = packaging_info new_values["packaging_info"] = packaging_info
return new_values return new_values
def _get_search_domain(
self, search, category, attrib_values, search_in_description=True
):
# AND across all selected attribute values instead of OR within same attribute
domain = super()._get_search_domain(search, category, [], search_in_description)
if attrib_values:
attr_domains = [
[("attribute_line_ids.value_ids", "in", [v[1]])] for v in attrib_values
]
domain = expression.AND([domain] + attr_domains)
return domain
def _get_search_order(self, post):
# is_published is company_dependent and has no DB column, so it cannot
# be used in ORDER BY. The shop domain already filters published products,
# so dropping it from the sort has no visible effect for visitors.
order = (
post.get("order")
or request.env["website"].get_current_website().shop_default_sort
)
return "%s, id desc" % order
def _get_search_options( def _get_search_options(
self, self,
category=None, category=None,

View file

@ -8,7 +8,7 @@ class ProductTemplate(models.Model):
copy=False, copy=False,
default=lambda self: self._default_is_published(), default=lambda self: self._default_is_published(),
index=True, index=True,
company_dependent=True, company_dependent=True, # added
) )
def action_publish_on_website(self): def action_publish_on_website(self):
@ -20,9 +20,16 @@ class ProductTemplate(models.Model):
product.is_published = False product.is_published = False
def _search_get_detail(self, website, order, options): def _search_get_detail(self, website, order, options):
search_details = super()._search_get_detail(website, order, options) # Pass attrib_values as empty so the parent builds no attribute filter,
# then add one condition per value to enforce AND semantics.
attrib_values = options.get("attrib_values")
patched_options = dict(options, attrib_values=[]) if attrib_values else options
search_details = super()._search_get_detail(website, order, patched_options)
open_product_ids = options.get("open_product_ids") open_product_ids = options.get("open_product_ids")
domain = search_details["base_domain"] domain = search_details["base_domain"]
if open_product_ids is not None: if open_product_ids is not None:
domain.append([("id", "in", tuple(open_product_ids))]) domain.append([("id", "in", tuple(open_product_ids))])
if attrib_values:
for value in attrib_values:
domain.append([("attribute_line_ids.value_ids", "in", [value[1]])])
return search_details return search_details

View file

@ -1,13 +1,16 @@
:0.0.1: Initial module. - 16.0.0.0.1: Initial module.
:1.0.0: Live - 16.0.1.0.0: Live
:1.1.0: Add Open Packagings Filter and fix rounding issue - 16.0.1.1.0: Add Open Packagings Filter and fix rounding issue
:1.1.1: Pass arguments to super again - 16.0.1.1.1: Pass arguments to super again
:1.1.2: Make publish on website multi company - 16.0.1.1.2: Make publish on website multi company
:1.2.0: Add Open Packages to Product list - 16.0.1.2.0: Add Open Packages to Product list
:1.3.0: Adapt new Open Packages format and add indicators - 16.0.1.3.0: Adapt new Open Packages format and add indicators
:1.3.1: Remove Max Qty from first page - 16.0.1.3.1: Remove Max Qty from first page
:1.3.2: Add decimal to ribbon - 16.0.1.3.2: Add decimal to ribbon
:1.4.0: Add credit warning and avoid debts option - 16.0.1.4.0: Add credit warning and avoid debts option
:1.4.1: Add credit to portal details - 16.0.1.4.1: Add credit to portal details
:1.4.2: Catch unescaped float values - 16.0.1.4.2: Catch unescaped float values
:1.4.3: Drop session if SO belongs to another website (Odoo 16 bug) - 16.0.1.4.3: Drop session if SO belongs to another website (Odoo 16 bug)
- 16.0.1.4.3: Remove is_published filtering and use job manager only on more than one
- 16.0.1.4.5: Make open Packagings more visible
- 16.0.1.5.0: Make attribute selection an AND-filter

View file

@ -397,38 +397,24 @@ adjustes the uom quantitiy</li>
</div> </div>
<div class="section" id="changelog"> <div class="section" id="changelog">
<h1><a class="toc-backref" href="#toc-entry-1">Changelog</a></h1> <h1><a class="toc-backref" href="#toc-entry-1">Changelog</a></h1>
<table class="docutils field-list" frame="void" rules="none"> <ul class="simple">
<col class="field-name" /> <li>16.0.0.0.1: Initial module.</li>
<col class="field-body" /> <li>16.0.1.0.0: Live</li>
<tbody valign="top"> <li>16.0.1.1.0: Add Open Packagings Filter and fix rounding issue</li>
<tr class="field"><th class="field-name">0.0.1:</th><td class="field-body">Initial module.</td> <li>16.0.1.1.1: Pass arguments to super again</li>
</tr> <li>16.0.1.1.2: Make publish on website multi company</li>
<tr class="field"><th class="field-name">1.0.0:</th><td class="field-body">Live</td> <li>16.0.1.2.0: Add Open Packages to Product list</li>
</tr> <li>16.0.1.3.0: Adapt new Open Packages format and add indicators</li>
<tr class="field"><th class="field-name">1.1.0:</th><td class="field-body">Add Open Packagings Filter and fix rounding issue</td> <li>16.0.1.3.1: Remove Max Qty from first page</li>
</tr> <li>16.0.1.3.2: Add decimal to ribbon</li>
<tr class="field"><th class="field-name">1.1.1:</th><td class="field-body">Pass arguments to super again</td> <li>16.0.1.4.0: Add credit warning and avoid debts option</li>
</tr> <li>16.0.1.4.1: Add credit to portal details</li>
<tr class="field"><th class="field-name">1.1.2:</th><td class="field-body">Make publish on website multi company</td> <li>16.0.1.4.2: Catch unescaped float values</li>
</tr> <li>16.0.1.4.3: Drop session if SO belongs to another website (Odoo 16 bug)</li>
<tr class="field"><th class="field-name">1.2.0:</th><td class="field-body">Add Open Packages to Product list</td> <li>16.0.1.4.3: Remove is_published filtering and use job manager only on more than one</li>
</tr> <li>16.0.1.4.5: Make open Packagings more visible</li>
<tr class="field"><th class="field-name">1.3.0:</th><td class="field-body">Adapt new Open Packages format and add indicators</td> <li>16.0.1.5.0: Make attribute selection an AND-filter</li>
</tr> </ul>
<tr class="field"><th class="field-name">1.3.1:</th><td class="field-body">Remove Max Qty from first page</td>
</tr>
<tr class="field"><th class="field-name">1.3.2:</th><td class="field-body">Add decimal to ribbon</td>
</tr>
<tr class="field"><th class="field-name">1.4.0:</th><td class="field-body">Add credit warning and avoid debts option</td>
</tr>
<tr class="field"><th class="field-name">1.4.1:</th><td class="field-body">Add credit to portal details</td>
</tr>
<tr class="field"><th class="field-name">1.4.2:</th><td class="field-body">Catch unescaped float values</td>
</tr>
<tr class="field"><th class="field-name">1.4.3:</th><td class="field-body">Drop session if SO belongs to another website (Odoo 16 bug)</td>
</tr>
</tbody>
</table>
</div> </div>
<div class="section" id="bug-tracker"> <div class="section" id="bug-tracker">
<h1><a class="toc-backref" href="#toc-entry-2">Bug Tracker</a></h1> <h1><a class="toc-backref" href="#toc-entry-2">Bug Tracker</a></h1>

View file

@ -2,7 +2,7 @@
<odoo> <odoo>
<template id="products_open_packaging" inherit_id="website_sale.products"> <template id="products_open_packaging" inherit_id="website_sale.products">
<xpath expr="//div[hasclass('products_attributes_filters')]" position="after"> <xpath expr="//div[hasclass('products_attributes_filters')]" position="before">
<div class="mt-3"> <div class="mt-3">
<a t-att-href="keep('/shop',open_packagings = False if open_packagings else True)"> <a t-att-href="keep('/shop',open_packagings = False if open_packagings else True)">
<h6 t-attf-class="o_packaging_collapse_title mb-3 {{_titleClasses}}"><b> <h6 t-attf-class="o_packaging_collapse_title mb-3 {{_titleClasses}}"><b>
@ -15,7 +15,7 @@
</template> </template>
<template id="o_wsale_offcanvas" name="Open Packagings" inherit_id="website_sale.o_wsale_offcanvas"> <template id="o_wsale_offcanvas" name="Open Packagings" inherit_id="website_sale.o_wsale_offcanvas">
<xpath expr="//t[@t-call='website_sale.filter_products_price']" position="before"> <xpath expr="//div[contains(@t-if,'opt_wsale_categories')]" position="before">
<div class="position-relative o_wsale_offcanvas_title px-4 border-top"> <div class="position-relative o_wsale_offcanvas_title px-4 border-top">
<a t-att-href="keep('/shop',open_packagings = False if open_packagings else True)"> <a t-att-href="keep('/shop',open_packagings = False if open_packagings else True)">
<h6 t-attf-class="o_packaging_collapse_title mb-3 pt-3 {{_titleClasses}}"><b> <h6 t-attf-class="o_packaging_collapse_title mb-3 pt-3 {{_titleClasses}}"><b>

View file

@ -41,6 +41,8 @@ Changelog
- 16.0.1.2.2: Enable Unit of Measure on installation - 16.0.1.2.2: Enable Unit of Measure on installation
- 16.0.1.3.0: Add GTIN Article field and update metadata retrieval - 16.0.1.3.0: Add GTIN Article field and update metadata retrieval
- 16.0.1.3.1: Temporay disable image_1920 field verification - 16.0.1.3.1: Temporay disable image_1920 field verification
- 16.0.1.3.2: Temporay disable image_1920 field verification for product.image
- 16.0.1.3.3: Add max size for bith image_1920 fields
Bug Tracker Bug Tracker
=========== ===========

View file

@ -4,7 +4,7 @@
"author": "BAKEUP", "author": "BAKEUP",
"website": "https://www.bakeup.org", "website": "https://www.bakeup.org",
"category": "Product", "category": "Product",
"version": "16.0.1.3.1", "version": "16.0.1.3.3",
"depends": ["nfu_product", "data_nature_importer"], "depends": ["nfu_product", "data_nature_importer"],
"data": ["data/ir_config_parameter.xml", "views/product_bnn_views.xml"], "data": ["data/ir_config_parameter.xml", "views/product_bnn_views.xml"],
"assets": {}, "assets": {},

View file

@ -1,2 +1,3 @@
from . import product_image
from . import product_product from . import product_product
from . import product_template from . import product_template

View file

@ -0,0 +1,10 @@
from odoo import fields, models
class ProductImage(models.Model):
_inherit = "product.image"
# TODO: Remove as soon as data_nature thumbnails work
image_1920 = fields.Image(
"Image", max_width=1920, max_height=1920, verify_resolution=False
)

View file

@ -7,7 +7,9 @@ class ProductTemplate(models.Model):
_inherit = "product.template" _inherit = "product.template"
# TODO: Remove as soon as data_nature thumbnails work # TODO: Remove as soon as data_nature thumbnails work
image_1920 = fields.Image(verify_resolution=False) image_1920 = fields.Image(
"Image", max_width=1920, max_height=1920, verify_resolution=False
)
manufacturer_abbr = fields.Char(string="Manufacturer Abbreviation") manufacturer_abbr = fields.Char(string="Manufacturer Abbreviation")
# Should be moved to product.product at some point # Should be moved to product.product at some point

View file

@ -7,3 +7,5 @@
- 16.0.1.2.2: Enable Unit of Measure on installation - 16.0.1.2.2: Enable Unit of Measure on installation
- 16.0.1.3.0: Add GTIN Article field and update metadata retrieval - 16.0.1.3.0: Add GTIN Article field and update metadata retrieval
- 16.0.1.3.1: Temporay disable image_1920 field verification - 16.0.1.3.1: Temporay disable image_1920 field verification
- 16.0.1.3.2: Temporay disable image_1920 field verification for product.image
- 16.0.1.3.3: Add max size for bith image_1920 fields

View file

@ -395,6 +395,8 @@ ul.auto-toc {
<li>16.0.1.2.2: Enable Unit of Measure on installation</li> <li>16.0.1.2.2: Enable Unit of Measure on installation</li>
<li>16.0.1.3.0: Add GTIN Article field and update metadata retrieval</li> <li>16.0.1.3.0: Add GTIN Article field and update metadata retrieval</li>
<li>16.0.1.3.1: Temporay disable image_1920 field verification</li> <li>16.0.1.3.1: Temporay disable image_1920 field verification</li>
<li>16.0.1.3.2: Temporay disable image_1920 field verification for product.image</li>
<li>16.0.1.3.3: Add max size for bith image_1920 fields</li>
</ul> </ul>
</div> </div>
<div class="section" id="bug-tracker"> <div class="section" id="bug-tracker">

View file

@ -1,77 +0,0 @@
================
Sale Order Batch
================
..
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:6686f0672ede8093b17d5d7b165f72f8e49341c7849f9e0d5a162e350fc36d71
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
:target: https://odoo-community.org/page/development-status
:alt: Beta
.. |badge2| image:: https://img.shields.io/badge/licence-LGPL--3-blue.png
:target: http://www.gnu.org/licenses/lgpl-3.0-standalone.html
:alt: License: LGPL-3
.. |badge3| image:: https://img.shields.io/badge/github-bakeupboys%2Fnfu-lightgray.png?logo=github
:target: https://github.com/bakeupboys/nfu/tree/16.0/sale_order_batch
:alt: bakeupboys/nfu
|badge1| |badge2| |badge3|
This module adds the possibility to group Sale orders into batches. Attributes of a Sale Order Batch will be written to all
Sale Order. Wenn attached to a batch they can be processed together. On the other hand they cannot be proccesed individual
anymore as log as they are part of a batch. Only Sale Orders in state Qutotation or Quotation Send can be attached to a batch.
**Table of contents**
.. contents::
:local:
Changelog
=========
- 16.0.0.0.1: Initial module.
- 16.0.0.1: Add Multi Company ability
- 16.0.0.2: rename product creation methode
- 16.0.0.3: Make validity_date persistent, add batches to SO views
- 16.0.0.4: Remove validity date
- 16.0.0.5: Move product logic to sale order lines
- 16.0.2.1.0: Add "In Progress" and "Cancelled" state
- 16.0.2.2.0: Add invoice status and menues
- 16.0.2.2.1: Add lst_price to view
- 16.0.2.3.0: Add "Reset to Open"
- 16.0.2.3.1: Remove Sale Order from batch on cancelation
Bug Tracker
===========
Bugs are tracked on `GitHub Issues <https://github.com/bakeupboys/nfu/issues>`_.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us to smash it by providing a detailed and welcomed
`feedback <https://github.com/bakeupboys/nfu/issues/new?body=module:%20sale_order_batch%0Aversion:%2016.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
Do not contact contributors directly about support or help with technical issues.
Credits
=======
Authors
~~~~~~~
* BAKEUP
Contributors
~~~~~~~~~~~~
* Niels Göttsch <niels@ziemlichoptimal.de>
Maintainers
~~~~~~~~~~~
This module is part of the `bakeupboys/nfu <https://github.com/bakeupboys/nfu/tree/16.0/sale_order_batch>`_ project on GitHub.
You are welcome to contribute.

View file

@ -1,2 +0,0 @@
from . import models
from . import wizard

View file

@ -1,20 +0,0 @@
{
"name": "Sale Order Batch",
"summary": "Group Sale Orders into a batch",
"author": "BAKEUP",
"website": "https://www.bakeup.org",
"category": "Sale",
"version": "16.0.2.3.1",
"depends": ["sale", "product"],
"data": [
"security/ir.model.access.csv",
"security/ir_rules.xml",
"views/sale_order_batch_views.xml",
"views/sale_order_batch_product_views.xml",
"views/sale_order_views.xml",
"views/sale_menus.xml",
"wizard/sale_order_batch_cancel_wizard.xml",
"data/ir_sequence_data.xml",
],
"license": "LGPL-3",
}

View file

@ -1,12 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo noupdate="1">
<record id="seq_sale_order_batch" model="ir.sequence">
<field name="name">Sales Order Batch</field>
<field name="code">sale.order.batch</field>
<field name="prefix">SOB</field>
<field name="padding">5</field>
<field name="company_id" eval="False"/>
</record>
</odoo>

View file

@ -1,4 +0,0 @@
from . import sale_order
from . import sale_order_batch
from . import sale_order_batch_product
from . import sale_order_line

View file

@ -1,75 +0,0 @@
from odoo import _, fields, models
from odoo.exceptions import UserError
class SaleOrder(models.Model):
_inherit = "sale.order"
batch_id = fields.Many2one(
"sale.order.batch",
copy=False,
check_company=True,
domain="[('state','!=','closed')]",
)
batch_state = fields.Selection(
related="batch_id.state",
string="Batch State",
)
def _get_current_batch(self):
"""
Returns the current batch of the sale order.
:return: sale.order.batch
"""
self.ensure_one()
return self.env["sale.order.batch"].search(
[("state", "=", "open"), ("company_id", "=", self.company_id.id)], limit=1
)
def action_add_to_batch(self):
"""
TODO: Add Wizard to choose between batches if more than one existis.
Picking the first one for now.
"""
for sale_order in self:
company = sale_order.company_id
sale_order = sale_order.with_company(company)
if not sale_order.batch_id and sale_order.state in ["draft", "sent"]:
batch = sale_order._get_current_batch()
if not batch:
batch = sale_order.env["sale.order.batch"].create({})
sale_order.batch_id = batch
def action_view_sale_order_batch(self):
return {
"name": _("Sale Order Batch"),
"type": "ir.actions.act_window",
"view_type": "form",
"view_mode": "form",
"res_model": "sale.order.batch",
"res_id": self.batch_id.id,
}
def action_confirm(self):
invalid_orders = []
if not self.env.context.get("bypass_batch", False):
for order in self:
if order.batch_id:
invalid_orders.append(order.name)
if invalid_orders:
raise UserError(
_(f"Sale Order belongs to a Batch: {', '.join(invalid_orders)}")
)
return super().action_confirm()
def action_cancel(self):
self.write({"batch_id": False})
res = super().action_cancel()
return res
def write(self, vals):
res = super().write(vals)
if "batch_id" in vals.keys():
for order in self:
order.order_line._update_batch_product()
return res

View file

@ -1,263 +0,0 @@
from odoo import _, api, fields, models
from odoo.exceptions import UserError
from odoo.addons.sale.models.sale_order import INVOICE_STATUS
STATES = [
("open", "Open"),
("in_progress", "In Progress"),
("closed", "Closed"),
("cancel", "Cancelled"),
]
READONLY_FIELD_STATES = {state: [("readonly", True)] for state in {"closed"}}
class SaleOrderBatch(models.Model):
"""Group serveral Sale Orders into a batch"""
_name = "sale.order.batch"
_description = "Sales Order Batch"
_inherit = ["mail.thread", "mail.activity.mixin"]
_order = "date_order desc, id desc"
_check_company_auto = True
name = fields.Char(
string="Order Batch Reference",
required=True,
copy=False,
readonly=True,
index="trigram",
default=lambda self: _("New"),
)
company_id = fields.Many2one(
comodel_name="res.company",
required=True,
index=True,
default=lambda self: self.env.company,
)
state = fields.Selection(
selection=STATES,
string="Status",
readonly=True,
copy=False,
index=True,
tracking=1,
default="open",
)
date_order = fields.Datetime(
string="Order Date",
required=True,
readonly=False,
copy=False,
states=READONLY_FIELD_STATES,
help="Creation date of order batch,\nConfirmation date of confirmed orders.",
default=fields.Datetime.now,
)
sale_order_ids = fields.One2many("sale.order", "batch_id")
sale_order_count = fields.Integer(compute="_compute_sale_order_count")
sale_order_line_ids = fields.One2many(
"sale.order.line", "batch_id", states=READONLY_FIELD_STATES
)
invoice_ids = fields.Many2many("account.move", compute="_compute_invoice_ids")
invoice_count = fields.Integer(compute="_compute_invoice_ids")
invoice_status = fields.Selection(
selection=INVOICE_STATUS, compute="_compute_invoice_status", store=True
)
amount_total = fields.Float(compute="_compute_amount_total", string="Total")
product_ids = fields.One2many("sale.order.batch.product", "batch_id")
product_count = fields.Integer(compute="_compute_product_count")
partner_credit_warning = fields.Text(compute="_compute_partner_credit_warning")
@api.depends("sale_order_ids")
def _compute_sale_order_count(self):
for batch in self:
batch.sale_order_count = len(batch.sale_order_ids)
@api.depends("sale_order_ids.invoice_ids")
def _compute_invoice_ids(self):
for batch in self:
invoices = batch.sale_order_ids.mapped("invoice_ids")
batch.invoice_ids = invoices
batch.invoice_count = len(invoices)
@api.depends("sale_order_ids.invoice_status")
def _compute_invoice_status(self):
for batch in self:
if batch.sale_order_ids and any(
invoice_status == "to invoice"
for invoice_status in batch.sale_order_ids.mapped("invoice_status")
):
batch.invoice_status = "to invoice"
elif batch.sale_order_ids and all(
invoice_status == "invoiced"
for invoice_status in batch.sale_order_ids.mapped("invoice_status")
):
batch.invoice_status = "invoiced"
elif batch.sale_order_ids and all(
invoice_status in ("invoiced", "upselling")
for invoice_status in batch.sale_order_ids.mapped("invoice_status")
):
batch.invoice_status = "upselling"
else:
batch.invoice_status = "no"
@api.depends("sale_order_ids.amount_total")
def _compute_amount_total(self):
for batch in self:
batch.amount_total = sum(batch.sale_order_ids.mapped("amount_total"))
@api.depends("product_ids")
def _compute_product_count(self):
for batch in self:
batch.product_count = len(batch.product_ids)
@api.depends("sale_order_ids.partner_credit_warning")
def _compute_partner_credit_warning(self):
for batch in self:
batch.partner_credit_warning = ""
for order in batch.sale_order_ids:
if order.partner_credit_warning:
batch.partner_credit_warning += order.partner_credit_warning + "\n"
def action_view_source_sale_orders(self):
self.ensure_one()
result = self.env["ir.actions.act_window"]._for_xml_id("sale.action_orders")
if len(self.sale_order_ids) > 1:
result["domain"] = [("id", "in", self.sale_order_ids.ids)]
elif len(self.sale_order_ids) == 1:
result["views"] = [(self.env.ref("sale.view_order_form", False).id, "form")]
result["res_id"] = self.sale_order_ids.id
else:
result = {"type": "ir.actions.act_window_close"}
return result
def action_view_products(self):
self.ensure_one()
result = self.env["ir.actions.act_window"]._for_xml_id(
"product.product_normal_action_sell"
)
if len(self.product_ids) > 1:
result["domain"] = [("id", "in", self.product_ids.ids)]
elif len(self.product_ids) == 1:
result["views"] = [
(self.env.ref("product.product_product_tree_view", False).id, "form")
]
result["res_id"] = self.product_ids.id
else:
result = {"type": "ir.actions.act_window_close"}
return result
def action_view_invoice(self):
invoices = self.mapped("invoice_ids")
action = self.env["ir.actions.actions"]._for_xml_id(
"account.action_move_out_invoice_type"
)
if len(invoices) > 1:
action["domain"] = [("id", "in", invoices.ids)]
elif len(invoices) == 1:
form_view = [(self.env.ref("account.view_move_form").id, "form")]
if "views" in action:
action["views"] = form_view + [
(state, view) for state, view in action["views"] if view != "form"
]
else:
action["views"] = form_view
action["res_id"] = invoices.id
else:
action = {"type": "ir.actions.act_window_close"}
action["context"] = {"default_move_type": "out_invoice"}
return action
def action_in_progress(self):
for batch in self:
batch.sale_order_line_ids._validate_analytic_distribution()
orders = batch.sale_order_ids
orders.update({"state": "sent"})
batch.update({"state": "in_progress"})
return True
def action_confirm(self):
for batch in self:
orders = batch.with_context(bypass_batch=True).sale_order_ids
orders.action_confirm()
batch.update({"state": "closed"})
return True
def action_cancel(self):
cancel_warning = self._show_cancel_wizard()
if cancel_warning:
self.ensure_one()
return {
"name": _("Cancel %s", self.name),
"view_mode": "form",
"type": "ir.actions.act_window",
"res_model": "sale.order.batch.cancel.wizard",
"target": "new",
"context": {"active_ids": self.ids},
}
else:
self._action_cancel()
def _action_cancel(self):
orders = self.mapped("sale_order_ids")
# use _action_cancel to skip the wizzard
orders._action_cancel()
self.write({"state": "cancel"})
def _show_cancel_wizard(self):
"""Decide whether the sale.order.batch.cancel wizard should be shown to cancel specified orders.
:return: True if there is any non-draft order in the given orders
:rtype: bool
"""
if self.env.context.get("disable_cancel_warning"):
return False
batch_orders = self.mapped("sale_order_ids")
return any(order._show_cancel_wizard() for order in batch_orders)
def action_open(self):
for batch in self:
orders = batch.sale_order_ids
orders.action_draft()
batch.update({"state": "open"})
return True
@api.model_create_multi
def create(self, vals_list):
for vals in vals_list:
if "company_id" in vals:
self = self.with_company(vals["company_id"])
if vals.get("name", _("New")) == _("New"):
vals["name"] = self.env["ir.sequence"].next_by_code(
"sale.order.batch"
) or _("New")
return super().create(vals_list)
class SaleOrderBatchCancelWizard(models.TransientModel):
_name = "sale.order.batch.cancel.wizard"
_description = "Sale Order Batch Cancel Wizard"
def action_cancel(self):
batches = self.env["sale.order.batch"].browse(self._context["active_ids"])
for batch in batches:
orders = batch.sale_order_ids
# Warn if any sale order is not in draft
not_draft_orders = orders.filtered(lambda o: o.state != "draft")
if not_draft_orders:
raise UserError(
_(
"You can only cancel batches where all sale orders are in draft state."
)
)
# use _action_cancel to skip the wizzard
# ToDo: Implement wizard logic
orders._action_cancel()
batch.update({"state": "cancel"})
return True
def action_discard(self):
return {"type": "ir.actions.act_window_close"}

View file

@ -1,103 +0,0 @@
from odoo import api, fields, models
from .sale_order_batch import READONLY_FIELD_STATES
class SaleOrderBatchProduct(models.Model):
"""Show Products available in Sale Order Batch"""
_name = "sale.order.batch.product"
_description = "Sale Order Batch Product"
batch_id = fields.Many2one(
comodel_name="sale.order.batch",
string="Batch Reference",
required=True,
ondelete="cascade",
index=True,
copy=False,
readonly=True,
)
state = fields.Selection(related="batch_id.state")
product_id = fields.Many2one(
comodel_name="product.product", required=True, readonly=True
)
product_template_id = fields.Many2one(
"product.template",
related="product_id.product_tmpl_id",
string="Product Template",
)
sale_order_line_ids = fields.One2many(
"sale.order.line", "batch_product_id", states=READONLY_FIELD_STATES
)
product_uom_category_id = fields.Many2one(
related="product_id.uom_id.category_id", depends=["product_id"]
)
product_uom_qty = fields.Float(
string="Quantity",
compute="_compute_uom_qty",
precompute=True,
store=True,
digits=[12, 3],
)
product_uom = fields.Many2one(related="product_id.uom_id")
lst_price = fields.Float(
"Sales Price",
related="product_id.lst_price",
digits="Product Price",
help="""The sale price is managed from the product template. Click on the
'Configure Variants' button to set the extra attribute prices.""",
)
product_packaging_id = fields.Many2one(
comodel_name="product.packaging",
string="Packaging",
compute="_compute_product_packaging_id",
store=True,
readonly=False,
precompute=True,
domain="[('sales', '=', True), ('product_id','=',product_id)]",
)
product_packaging_qty = fields.Float(compute="_compute_product_packaging_qty")
@api.constrains("product_id", "sale_order_line_ids")
def _check_sale_order_line_products(self):
for product in self:
if any(
line.product_id != product.product_id
for line in product.sale_order_line_ids
):
raise ValueError("Product must be the same for all Sale Order Lines")
@api.depends("sale_order_line_ids.product_uom_qty")
def _compute_uom_qty(self):
for product in self:
product.product_uom_qty = sum(
product.sale_order_line_ids.mapped("product_uom_qty")
)
@api.depends("product_id", "product_uom_qty", "product_uom")
def _compute_product_packaging_id(self):
for line in self:
# remove packaging if not match the product
if (
line.product_packaging_id
and line.product_packaging_id.product_id != line.product_id
):
line.product_packaging_id = False
# suggest always the first packaging
if line.product_id and line.product_uom_qty and line.product_uom:
suggested_packaging = (
line.product_id.packaging_ids[0]
if line.product_id.packaging_ids
else False
)
line.product_packaging_id = (
suggested_packaging or line.product_packaging_id
)
@api.depends("product_packaging_id")
def _compute_product_packaging_qty(self):
for product in self:
product.product_packaging_qty = (
product.product_packaging_id.qty if product.product_packaging_id else 1
)

View file

@ -1,57 +0,0 @@
from odoo import api, fields, models
class SaleOrderLine(models.Model):
_inherit = "sale.order.line"
batch_id = fields.Many2one(related="order_id.batch_id")
batch_product_id = fields.Many2one("sale.order.batch.product", ondelete="restrict")
def _link_batch_product(self):
lines_with_batch = self.filtered(lambda l: l.batch_id)
for line in lines_with_batch:
batch_id = line.batch_id
batch_product = self.env["sale.order.batch.product"].search(
[
("product_id", "=", line.product_id.id),
("batch_id", "=", batch_id.id),
],
limit=1,
)
if not batch_product:
batch_product = self.env["sale.order.batch.product"].create(
{"batch_id": batch_id.id, "product_id": line.product_id.id}
)
line.batch_product_id = batch_product
def _unlink_batch_product(self):
lines_to_unlink = self.filtered(lambda l: l.batch_product_id)
batch_products = lines_to_unlink.mapped("batch_product_id")
lines_to_unlink.update({"batch_product_id": False})
batch_products.filtered(lambda p: len(p.sale_order_line_ids) == 0).unlink()
def _update_batch_product(self):
lines_with_batch = self.filtered(lambda l: l.batch_id)
lines_without_batch = self - lines_with_batch
lines_with_batch.filtered(
lambda l: l.batch_product_id
and l.batch_product_id.product_id != l.product_id
)._unlink_batch_product()
lines_with_batch._link_batch_product()
lines_without_batch._unlink_batch_product()
@api.model_create_multi
def create(self, vals_list):
res = super().create(vals_list)
res._link_batch_product()
return res
def write(self, vals):
res = super().write(vals)
if vals.get("product_id"):
self._update_batch_product()
return res
def unlink(self):
self._unlink_batch_product()
return super().unlink()

View file

@ -1 +0,0 @@
* Niels Göttsch <niels@ziemlichoptimal.de>

View file

@ -1,3 +0,0 @@
This module adds the possibility to group Sale orders into batches. Attributes of a Sale Order Batch will be written to all
Sale Order. Wenn attached to a batch they can be processed together. On the other hand they cannot be proccesed individual
anymore as log as they are part of a batch. Only Sale Orders in state Qutotation or Quotation Send can be attached to a batch.

View file

@ -1,11 +0,0 @@
- 16.0.0.0.1: Initial module.
- 16.0.0.1: Add Multi Company ability
- 16.0.0.2: rename product creation methode
- 16.0.0.3: Make validity_date persistent, add batches to SO views
- 16.0.0.4: Remove validity date
- 16.0.0.5: Move product logic to sale order lines
- 16.0.2.1.0: Add "In Progress" and "Cancelled" state
- 16.0.2.2.0: Add invoice status and menues
- 16.0.2.2.1: Add lst_price to view
- 16.0.2.3.0: Add "Reset to Open"
- 16.0.2.3.1: Remove Sale Order from batch on cancelation

View file

@ -1,6 +0,0 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_sale_order_batch_invoicing_payments,sale.order.batch,model_sale_order_batch,account.group_account_invoice,1,1,0,0
access_sale_order_batch,sale.order.batch,model_sale_order_batch,sales_team.group_sale_salesman,1,1,1,0
access_sale_order_batch_manager,sale.order.batch.manager,model_sale_order_batch,sales_team.group_sale_manager,1,1,1,1
access_sale_order_batch_product,sale.order.batch.product,model_sale_order_batch_product,sales_team.group_sale_salesman,1,1,1,1
sale_order_batch.access_sale_order_batch_cancel_wizard,access_sale_order_batch_cancel_wizard,sale_order_batch.model_sale_order_batch_cancel_wizard,base.group_user,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_sale_order_batch_invoicing_payments sale.order.batch model_sale_order_batch account.group_account_invoice 1 1 0 0
3 access_sale_order_batch sale.order.batch model_sale_order_batch sales_team.group_sale_salesman 1 1 1 0
4 access_sale_order_batch_manager sale.order.batch.manager model_sale_order_batch sales_team.group_sale_manager 1 1 1 1
5 access_sale_order_batch_product sale.order.batch.product model_sale_order_batch_product sales_team.group_sale_salesman 1 1 1 1
6 sale_order_batch.access_sale_order_batch_cancel_wizard access_sale_order_batch_cancel_wizard sale_order_batch.model_sale_order_batch_cancel_wizard base.group_user 1 1 1 1

View file

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo noupdate="1">
<!-- Multi - Company Rules -->
<record id="sale_order_batch_comp_rule" model="ir.rule">
<field name="name">Sales Order Batch multi-company</field>
<field name="model_id" ref="model_sale_order_batch"/>
<field name="domain_force">[('company_id', 'in', company_ids)]</field>
</record>
</odoo>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

View file

@ -1,435 +0,0 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="generator" content="Docutils: https://docutils.sourceforge.io/" />
<title>Sale Order Batch</title>
<style type="text/css">
/*
:Author: David Goodger (goodger@python.org)
:Id: $Id: html4css1.css 9511 2024-01-13 09:50:07Z milde $
:Copyright: This stylesheet has been placed in the public domain.
Default cascading style sheet for the HTML output of Docutils.
Despite the name, some widely supported CSS2 features are used.
See https://docutils.sourceforge.io/docs/howto/html-stylesheets.html for how to
customize this style sheet.
*/
/* used to remove borders from tables and images */
.borderless, table.borderless td, table.borderless th {
border: 0 }
table.borderless td, table.borderless th {
/* Override padding for "table.docutils td" with "! important".
The right padding separates the table cells. */
padding: 0 0.5em 0 0 ! important }
.first {
/* Override more specific margin styles with "! important". */
margin-top: 0 ! important }
.last, .with-subtitle {
margin-bottom: 0 ! important }
.hidden {
display: none }
.subscript {
vertical-align: sub;
font-size: smaller }
.superscript {
vertical-align: super;
font-size: smaller }
a.toc-backref {
text-decoration: none ;
color: black }
blockquote.epigraph {
margin: 2em 5em ; }
dl.docutils dd {
margin-bottom: 0.5em }
object[type="image/svg+xml"], object[type="application/x-shockwave-flash"] {
overflow: hidden;
}
/* Uncomment (and remove this text!) to get bold-faced definition list terms
dl.docutils dt {
font-weight: bold }
*/
div.abstract {
margin: 2em 5em }
div.abstract p.topic-title {
font-weight: bold ;
text-align: center }
div.admonition, div.attention, div.caution, div.danger, div.error,
div.hint, div.important, div.note, div.tip, div.warning {
margin: 2em ;
border: medium outset ;
padding: 1em }
div.admonition p.admonition-title, div.hint p.admonition-title,
div.important p.admonition-title, div.note p.admonition-title,
div.tip p.admonition-title {
font-weight: bold ;
font-family: sans-serif }
div.attention p.admonition-title, div.caution p.admonition-title,
div.danger p.admonition-title, div.error p.admonition-title,
div.warning p.admonition-title, .code .error {
color: red ;
font-weight: bold ;
font-family: sans-serif }
/* Uncomment (and remove this text!) to get reduced vertical space in
compound paragraphs.
div.compound .compound-first, div.compound .compound-middle {
margin-bottom: 0.5em }
div.compound .compound-last, div.compound .compound-middle {
margin-top: 0.5em }
*/
div.dedication {
margin: 2em 5em ;
text-align: center ;
font-style: italic }
div.dedication p.topic-title {
font-weight: bold ;
font-style: normal }
div.figure {
margin-left: 2em ;
margin-right: 2em }
div.footer, div.header {
clear: both;
font-size: smaller }
div.line-block {
display: block ;
margin-top: 1em ;
margin-bottom: 1em }
div.line-block div.line-block {
margin-top: 0 ;
margin-bottom: 0 ;
margin-left: 1.5em }
div.sidebar {
margin: 0 0 0.5em 1em ;
border: medium outset ;
padding: 1em ;
background-color: #ffffee ;
width: 40% ;
float: right ;
clear: right }
div.sidebar p.rubric {
font-family: sans-serif ;
font-size: medium }
div.system-messages {
margin: 5em }
div.system-messages h1 {
color: red }
div.system-message {
border: medium outset ;
padding: 1em }
div.system-message p.system-message-title {
color: red ;
font-weight: bold }
div.topic {
margin: 2em }
h1.section-subtitle, h2.section-subtitle, h3.section-subtitle,
h4.section-subtitle, h5.section-subtitle, h6.section-subtitle {
margin-top: 0.4em }
h1.title {
text-align: center }
h2.subtitle {
text-align: center }
hr.docutils {
width: 75% }
img.align-left, .figure.align-left, object.align-left, table.align-left {
clear: left ;
float: left ;
margin-right: 1em }
img.align-right, .figure.align-right, object.align-right, table.align-right {
clear: right ;
float: right ;
margin-left: 1em }
img.align-center, .figure.align-center, object.align-center {
display: block;
margin-left: auto;
margin-right: auto;
}
table.align-center {
margin-left: auto;
margin-right: auto;
}
.align-left {
text-align: left }
.align-center {
clear: both ;
text-align: center }
.align-right {
text-align: right }
/* reset inner alignment in figures */
div.align-right {
text-align: inherit }
/* div.align-center * { */
/* text-align: left } */
.align-top {
vertical-align: top }
.align-middle {
vertical-align: middle }
.align-bottom {
vertical-align: bottom }
ol.simple, ul.simple {
margin-bottom: 1em }
ol.arabic {
list-style: decimal }
ol.loweralpha {
list-style: lower-alpha }
ol.upperalpha {
list-style: upper-alpha }
ol.lowerroman {
list-style: lower-roman }
ol.upperroman {
list-style: upper-roman }
p.attribution {
text-align: right ;
margin-left: 50% }
p.caption {
font-style: italic }
p.credits {
font-style: italic ;
font-size: smaller }
p.label {
white-space: nowrap }
p.rubric {
font-weight: bold ;
font-size: larger ;
color: maroon ;
text-align: center }
p.sidebar-title {
font-family: sans-serif ;
font-weight: bold ;
font-size: larger }
p.sidebar-subtitle {
font-family: sans-serif ;
font-weight: bold }
p.topic-title {
font-weight: bold }
pre.address {
margin-bottom: 0 ;
margin-top: 0 ;
font: inherit }
pre.literal-block, pre.doctest-block, pre.math, pre.code {
margin-left: 2em ;
margin-right: 2em }
pre.code .ln { color: gray; } /* line numbers */
pre.code, code { background-color: #eeeeee }
pre.code .comment, code .comment { color: #5C6576 }
pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold }
pre.code .literal.string, code .literal.string { color: #0C5404 }
pre.code .name.builtin, code .name.builtin { color: #352B84 }
pre.code .deleted, code .deleted { background-color: #DEB0A1}
pre.code .inserted, code .inserted { background-color: #A3D289}
span.classifier {
font-family: sans-serif ;
font-style: oblique }
span.classifier-delimiter {
font-family: sans-serif ;
font-weight: bold }
span.interpreted {
font-family: sans-serif }
span.option {
white-space: nowrap }
span.pre {
white-space: pre }
span.problematic, pre.problematic {
color: red }
span.section-subtitle {
/* font-size relative to parent (h1..h6 element) */
font-size: 80% }
table.citation {
border-left: solid 1px gray;
margin-left: 1px }
table.docinfo {
margin: 2em 4em }
table.docutils {
margin-top: 0.5em ;
margin-bottom: 0.5em }
table.footnote {
border-left: solid 1px black;
margin-left: 1px }
table.docutils td, table.docutils th,
table.docinfo td, table.docinfo th {
padding-left: 0.5em ;
padding-right: 0.5em ;
vertical-align: top }
table.docutils th.field-name, table.docinfo th.docinfo-name {
font-weight: bold ;
text-align: left ;
white-space: nowrap ;
padding-left: 0 }
/* "booktabs" style (no vertical lines) */
table.docutils.booktabs {
border: 0px;
border-top: 2px solid;
border-bottom: 2px solid;
border-collapse: collapse;
}
table.docutils.booktabs * {
border: 0px;
}
table.docutils.booktabs th {
border-bottom: thin solid;
text-align: left;
}
h1 tt.docutils, h2 tt.docutils, h3 tt.docutils,
h4 tt.docutils, h5 tt.docutils, h6 tt.docutils {
font-size: 100% }
ul.auto-toc {
list-style-type: none }
</style>
</head>
<body>
<div class="document" id="sale-order-batch">
<h1 class="title">Sale Order Batch</h1>
<!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:6686f0672ede8093b17d5d7b165f72f8e49341c7849f9e0d5a162e350fc36d71
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
<p><a class="reference external image-reference" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external image-reference" href="http://www.gnu.org/licenses/lgpl-3.0-standalone.html"><img alt="License: LGPL-3" src="https://img.shields.io/badge/licence-LGPL--3-blue.png" /></a> <a class="reference external image-reference" href="https://github.com/bakeupboys/nfu/tree/16.0/sale_order_batch"><img alt="bakeupboys/nfu" src="https://img.shields.io/badge/github-bakeupboys%2Fnfu-lightgray.png?logo=github" /></a></p>
<p>This module adds the possibility to group Sale orders into batches. Attributes of a Sale Order Batch will be written to all
Sale Order. Wenn attached to a batch they can be processed together. On the other hand they cannot be proccesed individual
anymore as log as they are part of a batch. Only Sale Orders in state Qutotation or Quotation Send can be attached to a batch.</p>
<p><strong>Table of contents</strong></p>
<div class="contents local topic" id="contents">
<ul class="simple">
<li><a class="reference internal" href="#changelog" id="toc-entry-1">Changelog</a></li>
<li><a class="reference internal" href="#bug-tracker" id="toc-entry-2">Bug Tracker</a></li>
<li><a class="reference internal" href="#credits" id="toc-entry-3">Credits</a><ul>
<li><a class="reference internal" href="#authors" id="toc-entry-4">Authors</a></li>
<li><a class="reference internal" href="#contributors" id="toc-entry-5">Contributors</a></li>
<li><a class="reference internal" href="#maintainers" id="toc-entry-6">Maintainers</a></li>
</ul>
</li>
</ul>
</div>
<div class="section" id="changelog">
<h1><a class="toc-backref" href="#toc-entry-1">Changelog</a></h1>
<ul class="simple">
<li>16.0.0.0.1: Initial module.</li>
<li>16.0.0.1: Add Multi Company ability</li>
<li>16.0.0.2: rename product creation methode</li>
<li>16.0.0.3: Make validity_date persistent, add batches to SO views</li>
<li>16.0.0.4: Remove validity date</li>
<li>16.0.0.5: Move product logic to sale order lines</li>
<li>16.0.2.1.0: Add “In Progress” and “Cancelled” state</li>
<li>16.0.2.2.0: Add invoice status and menues</li>
<li>16.0.2.2.1: Add lst_price to view</li>
<li>16.0.2.3.0: Add “Reset to Open”</li>
<li>16.0.2.3.1: Remove Sale Order from batch on cancelation</li>
</ul>
</div>
<div class="section" id="bug-tracker">
<h1><a class="toc-backref" href="#toc-entry-2">Bug Tracker</a></h1>
<p>Bugs are tracked on <a class="reference external" href="https://github.com/bakeupboys/nfu/issues">GitHub Issues</a>.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us to smash it by providing a detailed and welcomed
<a class="reference external" href="https://github.com/bakeupboys/nfu/issues/new?body=module:%20sale_order_batch%0Aversion:%2016.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**">feedback</a>.</p>
<p>Do not contact contributors directly about support or help with technical issues.</p>
</div>
<div class="section" id="credits">
<h1><a class="toc-backref" href="#toc-entry-3">Credits</a></h1>
<div class="section" id="authors">
<h2><a class="toc-backref" href="#toc-entry-4">Authors</a></h2>
<ul class="simple">
<li>BAKEUP</li>
</ul>
</div>
<div class="section" id="contributors">
<h2><a class="toc-backref" href="#toc-entry-5">Contributors</a></h2>
<ul class="simple">
<li>Niels Göttsch &lt;<a class="reference external" href="mailto:niels&#64;ziemlichoptimal.de">niels&#64;ziemlichoptimal.de</a>&gt;</li>
</ul>
</div>
<div class="section" id="maintainers">
<h2><a class="toc-backref" href="#toc-entry-6">Maintainers</a></h2>
<p>This module is part of the <a class="reference external" href="https://github.com/bakeupboys/nfu/tree/16.0/sale_order_batch">bakeupboys/nfu</a> project on GitHub.</p>
<p>You are welcome to contribute.</p>
</div>
</div>
</div>
</body>
</html>

View file

@ -1,25 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<menuitem
id="menu_quotation_batch"
action="action_quotation_batch"
parent="sale.sale_order_menu"
groups="sales_team.group_sale_salesman"
sequence="15"
/>
<menuitem
id="menu_sale_order_batch"
action="action_order_batch"
parent="sale.sale_order_menu"
groups="sales_team.group_sale_salesman"
sequence="25"
/>
<menuitem
id="menu_sale_order_batch_invoice"
action="action_batch_to_invoice"
parent="sale.menu_sale_invoicing"
sequence="15"
/>
</odoo>

View file

@ -1,49 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<record id="view_order_batch_product_form" model="ir.ui.view">
<field name="name">sale.order.batch.product.form</field>
<field name="model">sale.order.batch.product</field>
<field name="arch" type="xml">
<form>
<header>
<field name="state" invisible="1"/>
</header>
<sheet>
<div class="oe_button_box" name="button_box">
</div>
<h1>
<field name="product_id"/>
</h1>
<group>
<group name="product_info">
<field name="batch_id"/>
<field name="product_uom_qty"/>
</group>
<group name="packaging_info">
<field name="product_packaging_id"/>
<field name="product_packaging_qty"/>
</group>
</group>
<notebook>
<page string="Sale Order Lines" name="order_line">
<field name="sale_order_line_ids" widget="section_and_note_one2many">
<tree editable="bottom" create="0">
<field name="company_id" invisible="1"/>
<field name="sequence" widget="handle"/>
<field name="order_id"/>
<field name="order_partner_id"/>
<field name="product_template_id" optional="hide"/>
<field name="product_id" optional="hide"/>
<field name="name"/>
<field name="product_uom_qty"/>
<field name="price_unit"/>
<field name="price_subtotal"/>
</tree>
</field>
</page>
</notebook>
</sheet>
</form>
</field>
</record>
</odoo>

View file

@ -1,279 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<record id="view_order_batch_tree" model="ir.ui.view">
<field name="name">sale.order.batch.tree</field>
<field name="model">sale.order.batch</field>
<field name="arch" type="xml">
<tree>
<field name="name"/>
<field name="date_order"/>
<field name="company_id" groups="base.group_multi_company" optional="show" readonly="1"/>
<field name="company_id" groups="!base.group_multi_company" invisible="1"/>
<field name="sale_order_ids" widget="many2many_tags"/>
<field
name="state"
decoration-success="state == 'sale' or state == 'done'"
decoration-info="state == 'draft' or state == 'sent'"
widget="badge"
optional="hide"
/>
<field
name="invoice_status"
decoration-success="invoice_status == 'invoiced'"
decoration-info="invoice_status == 'to invoice'"
decoration-warning="invoice_status == 'upselling'"
widget="badge"
optional="show"
/>
</tree>
</field>
</record>
<record id="view_order_batch_quotation_tree" model="ir.ui.view">
<field name="name">sale.order.batch.quotation.tree</field>
<field name="model">sale.order.batch</field>
<field name="arch" type="xml">
<tree>
<field name="name"/>
<field name="date_order"/>
<field name="company_id" groups="base.group_multi_company" optional="show" readonly="1"/>
<field name="company_id" groups="!base.group_multi_company" invisible="1"/>
<field name="sale_order_ids" widget="many2many_tags"/>
<field
name="state"
decoration-success="state == 'sale' or state == 'done'"
decoration-info="state == 'draft' or state == 'sent'"
widget="badge"
optional="show"
/>
</tree>
</field>
</record>
<record id="view_order_batch_form" model="ir.ui.view">
<field name="name">sale.order.batch.form</field>
<field name="model">sale.order.batch</field>
<field name="arch" type="xml">
<form>
<header>
<button
name="action_in_progress"
id="action_in_progress"
data-hotkey="g"
string="In Progress"
class="btn-primary"
type="object"
attrs="{'invisible': [('state', 'not in',['open'])]}"
/>
<button
name="action_confirm"
id="action_confirm"
data-hotkey="v"
string="Confirm"
class="btn-secondary"
type="object"
attrs="{'invisible': [('state', 'not in',['open'])]}"
/>
<button
name="action_confirm"
id="action_confirm"
data-hotkey="v"
string="Confirm"
class="btn-primary"
type="object"
attrs="{'invisible': [('state', 'not in',['in_progress'])]}"
/>
<button
id="create_invoices"
name="%(sale.action_view_sale_advance_payment_inv)d"
string="Create Invoices"
type="action"
class="btn-primary"
context="{
'default_sale_order_ids': sale_order_ids,
}"
data-hotkey="q"
attrs="{'invisible': [('state', 'not in','closed')]}"
/>
<button
name="action_cancel"
id="action_cancel"
data-hotkey="z"
string="Cancel"
class="btn-secondary"
type="object"
attrs="{'invisible': [('state', 'not in',['open', 'in_progress','closed'])]}"
/>
<button
name="action_open"
id="action_open"
data-hotkey="w"
string="Set to Open"
class="btn-primary"
type="object"
states="cancel"
/>
<field name="state" widget="statusbar"/>
</header>
<div
class="alert alert-warning mb-0"
role="alert"
attrs="{'invisible': [('partner_credit_warning', '=', '')]}"
>
<field name="partner_credit_warning"/>
</div>
<sheet>
<div class="oe_button_box" name="button_box">
<button
class="oe_stat_button"
type="object"
name="action_view_source_sale_orders"
icon="fa-pencil-square-o"
attrs="{'invisible': [('sale_order_count', '=', 0)]}"
>
<field string="Sale Orders" name="sale_order_count" widget="statinfo"/>
</button>
<button
class="oe_stat_button"
type="object"
name="action_view_products"
icon="fa-cart-plus"
attrs="{'invisible': [('product_count', '=', 0)]}"
>
<field string="Products" name="product_count" widget="statinfo"/>
</button>
<button
name="action_view_invoice"
type="object"
class="oe_stat_button"
icon="fa-pencil-square-o"
attrs="{'invisible': [('invoice_count', '=', 0)]}"
>
<field name="invoice_count" widget="statinfo" string="Invoices"/>
</button>
</div>
<h1>
<field name="name"/>
</h1>
<group>
<group>
<field name="amount_total" colspan="2" readonly="1"/>
</group>
<group>
<field name="date_order"/>
<field name="company_id" options="{'no_create': True}" groups="base.group_multi_company"/>
<field name="company_id" invisible="1" groups="!base.group_multi_company"/>
</group>
</group>
<notebook>
<page string="Sale Orders" name="orders">
<field name="state" invisible="1"/>
<field name="sale_order_ids">
<tree delete="0" editable="0" create="0">
<field name="name"/>
<field name="partner_id" readonly="1"/>
<field name="date_order" readonly="1"/>
<field name="amount_untaxed"/>
<field name="amount_total"/>
<field name="company_id" invisible="1"/>
<field
name="state"
decoration-success="state == 'sale' or state == 'done'"
decoration-info="state == 'draft' or state == 'sent'"
widget="badge"
optional="show"
/>
<field
name="invoice_status"
decoration-success="invoice_status == 'invoiced'"
decoration-info="invoice_status == 'to invoice'"
decoration-warning="invoice_status == 'upselling'"
widget="badge"
optional="hide"
/>
</tree>
</field>
</page>
<page string="Sale Order Lines" name="order_line">
<field name="sale_order_line_ids" widget="section_and_note_one2many">
<tree create="0" editable="bottom">
<field name="sequence" widget="handle"/>
<field name="order_id"/>
<field name="order_partner_id"/>
<field name="product_template_id" optional="hide"/>
<field name="product_id" optional="hide"/>
<field name="name"/>
<field name="product_uom_qty"/>
<field name="company_id" invisible="1"/>
<field name="price_unit"/>
<field name="price_subtotal"/>
</tree>
</field>
</page>
<page string="Products" name="products">
<field name="product_ids">
<tree create="0" delete="0">
<field name="product_template_id" optional="hide"/>
<field name="product_id"/>
<field name="product_uom_qty"/>
<field name="lst_price"/>
<field name="product_packaging_qty" groups="product.group_stock_packaging"/>
<field name="product_packaging_id" groups="product.group_stock_packaging"/>
</tree>
</field>
</page>
</notebook>
</sheet>
<div class="oe_chatter">
<field name="message_follower_ids" widget="mail_followers"/>
<field name="message_ids" widget="mail_thread"/>
</div>
</form>
</field>
</record>
<record id="sale_order_batch_view_search" model="ir.ui.view">
<field name="name">sale.order.batch.view.search</field>
<field name="model">sale.order.batch</field>
<field name="arch" type="xml">
<search>
<filter string="Active" name="filter_batch_open" domain="[('state', 'in', ['open', 'in_progress'])]"/>
<filter string="Finished" name="filter_batch_finished" domain="[('state', '=', 'closed')]"/>
<filter string="Cancelled" name="filter_batch_cancel" domain="[('state', '=', 'cancel')]"/>
</search>
</field>
</record>
<record id="action_quotation_batch" model="ir.actions.act_window">
<field name="name">Quotation Batches</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">sale.order.batch</field>
<field name="view_mode">tree,form,activity</field>
<field name="view_id" ref="view_order_batch_quotation_tree"/>
<field name="context">{'search_default_filter_batch_open': 1}</field>
</record>
<record id="action_order_batch" model="ir.actions.act_window">
<field name="name">Order Batches</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">sale.order.batch</field>
<field name="view_mode">tree,form,activity</field>
<field name="view_id" ref="view_order_batch_tree"/>
<field name="context">{'search_default_filter_batch_finished': 1}</field>
</record>
<record id="action_batch_to_invoice" model="ir.actions.act_window">
<field name="name">Batches to Invoice</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">sale.order.batch</field>
<field name="view_mode">tree,form,activity</field>
<field name="context">{'create': False}</field>
<field name="domain">[('invoice_status','=','to invoice')]</field>
<field name="search_view_id" ref="sale_order_batch_view_search"/>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
No batches to invoice found
</p>
</field>
</record>
</odoo>

View file

@ -1,72 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="view_order_form" model="ir.ui.view">
<field name="name">sale.order.form.inherit.batch</field>
<field name="model">sale.order</field>
<field name="inherit_id" ref="sale.view_order_form"/>
<field name="arch" type="xml">
<xpath expr="//button[@name='action_draft']" position="after">
<field name="batch_id" invisible="True"/>
<button
name="action_add_to_batch"
type="object"
string="Add to Batch"
data-hotkey="b"
attrs="{'invisible': ['|',('batch_id', '!=', False),('state','not in',['draft', 'sent'])]}"
/>
</xpath>
<xpath expr="//div[@name='button_box']" position="inside">
<button
class="oe_stat_button"
name="action_view_sale_order_batch"
type="object"
icon="fa-tasks"
attrs="{'invisible': [('batch_id', '=', False)]}"
>
<field name="batch_id" attrs="{'readonly': True}"/>
</button>
</xpath>
<xpath expr="//button[@name='action_confirm']" position="attributes">
<attribute
name="attrs"
>{'invisible': ['|',('batch_id','!=',False),('state', 'not in', ['sent'])]}</attribute>
</xpath>
<xpath expr="//button[@name='action_confirm'][2]" position="attributes">
<attribute
name="attrs"
>{'invisible': ['|',('batch_id','!=',False),('state', 'not in', ['draft'])]}</attribute>
</xpath>
<group name="partner_details" position="inside">
<field name="batch_id"/>
</group>
</field>
</record>
<record id="view_quatation_tree_inherit_batch" model="ir.ui.view">
<field name="name">sale.order.tree.inherited.batch</field>
<field name="model">sale.order</field>
<field name="inherit_id" ref="sale.view_quotation_tree"/>
<field name="arch" type="xml">
<xpath expr="//tree" position="inside">
<header>
<button type="object" name="action_add_to_batch" string="Add to Batch"/>
</header>
</xpath>
<xpath expr="//field[@name='name']" position="after">
<field name="batch_id"/>
</xpath>
</field>
</record>
<record id="view_order_tree_inherit_batch" model="ir.ui.view">
<field name="name">sale.order.tree.inherited.batch</field>
<field name="model">sale.order</field>
<field name="inherit_id" ref="sale.view_order_tree"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='name']" position="after">
<field name="batch_id"/>
</xpath>
</field>
</record>
</odoo>

View file

@ -1 +0,0 @@
from . import sale_order_batch_cancel_wizard

View file

@ -1,34 +0,0 @@
from odoo import api, fields, models
class SaleOrderBatchCancelWizard(models.TransientModel):
_name = "sale.order.batch.cancel.wizard"
_description = "Cancel Sale Order Batch Confirmation"
batch_ids = fields.Many2many(
comodel_name="sale.order.batch",
string="Batches to Cancel",
default=lambda self: self.env["sale.order.batch"].browse(
self.env.context.get("active_ids", [])
),
)
order_ids = fields.Many2many(
comodel_name="sale.order",
string="Orders to Cancel",
compute="_compute_order_ids",
)
@api.depends("batch_ids")
def _compute_order_ids(self):
for record in self:
batches = record.batch_ids
record.order_ids = batches.mapped("sale_order_ids")
def action_cancel(self):
batches = self.batch_ids
batches._action_cancel()
return {"type": "ir.actions.act_window_close"}
def action_discard(self):
return {"type": "ir.actions.act_window_close"}

View file

@ -1,24 +0,0 @@
<odoo>
<record id="view_sale_order_batch_cancel_wizard_form" model="ir.ui.view">
<field name="name">sale.order.batch.cancel.wizard.form</field>
<field name="model">sale.order.batch.cancel.wizard</field>
<field name="arch" type="xml">
<form>
<group col="1">
<div col="2" class="alert alert-warning" role="alert">
<span>Are you sure you want to cancel this order?</span>
</div>
<group col="2">
<field name="batch_ids" invisible="1"/>
<field name="order_ids" widget="many2many_tags"/>
</group>
</group>
<footer>
<button string="Cancel" name="action_cancel" type="object" class="btn-primary mx-1"/>
<button string="Discard" class="btn-secondary" special="cancel"/>
</footer>
</form>
</field>
</record>
</odoo>

View file

@ -1,65 +0,0 @@
==============================
Sale Order Batch Stock Binding
==============================
..
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:c32b4440b58e98f3bea5539d78dfa2916d64aebc757c140d78b67b2a2ce288da
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
:target: https://odoo-community.org/page/development-status
:alt: Beta
.. |badge2| image:: https://img.shields.io/badge/licence-LGPL--3-blue.png
:target: http://www.gnu.org/licenses/lgpl-3.0-standalone.html
:alt: License: LGPL-3
.. |badge3| image:: https://img.shields.io/badge/github-bakeupboys%2Fnfu-lightgray.png?logo=github
:target: https://github.com/bakeupboys/nfu/tree/16.0/sale_order_batch_stock
:alt: bakeupboys/nfu
|badge1| |badge2| |badge3|
This module adds Deliverys to Sale ORder Batch Views.
**Table of contents**
.. contents::
:local:
Changelog
=========
:0.0.1: Initial module.
Bug Tracker
===========
Bugs are tracked on `GitHub Issues <https://github.com/bakeupboys/nfu/issues>`_.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us to smash it by providing a detailed and welcomed
`feedback <https://github.com/bakeupboys/nfu/issues/new?body=module:%20sale_order_batch_stock%0Aversion:%2016.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
Do not contact contributors directly about support or help with technical issues.
Credits
=======
Authors
~~~~~~~
* BAKEUP
Contributors
~~~~~~~~~~~~
* Niels Göttsch <niels@ziemlichoptimal.de>
Maintainers
~~~~~~~~~~~
This module is part of the `bakeupboys/nfu <https://github.com/bakeupboys/nfu/tree/16.0/sale_order_batch_stock>`_ project on GitHub.
You are welcome to contribute.

View file

@ -1 +0,0 @@
from . import models

View file

@ -1,12 +0,0 @@
{
"name": "Sale Order Batch Stock Binding",
"summary": "Sale Order Batch Stock Bindingh",
"author": "BAKEUP",
"website": "https://www.bakeup.org",
"category": "hidden",
"version": "16.0.0.0.1",
"depends": ["sale_order_batch", "sale_stock"],
"data": ["views/sale_order_batch_views.xml"],
"autoinstall": "True",
"license": "LGPL-3",
}

View file

@ -1 +0,0 @@
from . import sale_order_batch

View file

@ -1,25 +0,0 @@
from odoo import api, fields, models
STATES = [("open", "Open"), ("close", "Close")]
class SaleOrderBatch(models.Model):
_inherit = "sale.order.batch"
picking_ids = fields.Many2many(
"stock.picking", compute="_compute_picking_ids", string="Transfers"
)
delivery_count = fields.Integer(
string="Delivery Orders", compute="_compute_picking_ids"
)
@api.depends("sale_order_ids.picking_ids")
def _compute_picking_ids(self):
for batch in self:
pickings = batch.sale_order_ids.mapped("picking_ids")
batch.picking_ids = pickings
batch.delivery_count = len(pickings)
def action_view_delivery(self):
return self.env["sale.order"]._get_action_view_picking(self.picking_ids)

View file

@ -1 +0,0 @@
* Niels Göttsch <niels@ziemlichoptimal.de>

View file

@ -1 +0,0 @@
This module adds Deliverys to Sale ORder Batch Views.

View file

@ -1 +0,0 @@
:0.0.1: Initial module.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

View file

@ -1,428 +0,0 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="generator" content="Docutils: https://docutils.sourceforge.io/" />
<title>Sale Order Batch Stock Binding</title>
<style type="text/css">
/*
:Author: David Goodger (goodger@python.org)
:Id: $Id: html4css1.css 9511 2024-01-13 09:50:07Z milde $
:Copyright: This stylesheet has been placed in the public domain.
Default cascading style sheet for the HTML output of Docutils.
Despite the name, some widely supported CSS2 features are used.
See https://docutils.sourceforge.io/docs/howto/html-stylesheets.html for how to
customize this style sheet.
*/
/* used to remove borders from tables and images */
.borderless, table.borderless td, table.borderless th {
border: 0 }
table.borderless td, table.borderless th {
/* Override padding for "table.docutils td" with "! important".
The right padding separates the table cells. */
padding: 0 0.5em 0 0 ! important }
.first {
/* Override more specific margin styles with "! important". */
margin-top: 0 ! important }
.last, .with-subtitle {
margin-bottom: 0 ! important }
.hidden {
display: none }
.subscript {
vertical-align: sub;
font-size: smaller }
.superscript {
vertical-align: super;
font-size: smaller }
a.toc-backref {
text-decoration: none ;
color: black }
blockquote.epigraph {
margin: 2em 5em ; }
dl.docutils dd {
margin-bottom: 0.5em }
object[type="image/svg+xml"], object[type="application/x-shockwave-flash"] {
overflow: hidden;
}
/* Uncomment (and remove this text!) to get bold-faced definition list terms
dl.docutils dt {
font-weight: bold }
*/
div.abstract {
margin: 2em 5em }
div.abstract p.topic-title {
font-weight: bold ;
text-align: center }
div.admonition, div.attention, div.caution, div.danger, div.error,
div.hint, div.important, div.note, div.tip, div.warning {
margin: 2em ;
border: medium outset ;
padding: 1em }
div.admonition p.admonition-title, div.hint p.admonition-title,
div.important p.admonition-title, div.note p.admonition-title,
div.tip p.admonition-title {
font-weight: bold ;
font-family: sans-serif }
div.attention p.admonition-title, div.caution p.admonition-title,
div.danger p.admonition-title, div.error p.admonition-title,
div.warning p.admonition-title, .code .error {
color: red ;
font-weight: bold ;
font-family: sans-serif }
/* Uncomment (and remove this text!) to get reduced vertical space in
compound paragraphs.
div.compound .compound-first, div.compound .compound-middle {
margin-bottom: 0.5em }
div.compound .compound-last, div.compound .compound-middle {
margin-top: 0.5em }
*/
div.dedication {
margin: 2em 5em ;
text-align: center ;
font-style: italic }
div.dedication p.topic-title {
font-weight: bold ;
font-style: normal }
div.figure {
margin-left: 2em ;
margin-right: 2em }
div.footer, div.header {
clear: both;
font-size: smaller }
div.line-block {
display: block ;
margin-top: 1em ;
margin-bottom: 1em }
div.line-block div.line-block {
margin-top: 0 ;
margin-bottom: 0 ;
margin-left: 1.5em }
div.sidebar {
margin: 0 0 0.5em 1em ;
border: medium outset ;
padding: 1em ;
background-color: #ffffee ;
width: 40% ;
float: right ;
clear: right }
div.sidebar p.rubric {
font-family: sans-serif ;
font-size: medium }
div.system-messages {
margin: 5em }
div.system-messages h1 {
color: red }
div.system-message {
border: medium outset ;
padding: 1em }
div.system-message p.system-message-title {
color: red ;
font-weight: bold }
div.topic {
margin: 2em }
h1.section-subtitle, h2.section-subtitle, h3.section-subtitle,
h4.section-subtitle, h5.section-subtitle, h6.section-subtitle {
margin-top: 0.4em }
h1.title {
text-align: center }
h2.subtitle {
text-align: center }
hr.docutils {
width: 75% }
img.align-left, .figure.align-left, object.align-left, table.align-left {
clear: left ;
float: left ;
margin-right: 1em }
img.align-right, .figure.align-right, object.align-right, table.align-right {
clear: right ;
float: right ;
margin-left: 1em }
img.align-center, .figure.align-center, object.align-center {
display: block;
margin-left: auto;
margin-right: auto;
}
table.align-center {
margin-left: auto;
margin-right: auto;
}
.align-left {
text-align: left }
.align-center {
clear: both ;
text-align: center }
.align-right {
text-align: right }
/* reset inner alignment in figures */
div.align-right {
text-align: inherit }
/* div.align-center * { */
/* text-align: left } */
.align-top {
vertical-align: top }
.align-middle {
vertical-align: middle }
.align-bottom {
vertical-align: bottom }
ol.simple, ul.simple {
margin-bottom: 1em }
ol.arabic {
list-style: decimal }
ol.loweralpha {
list-style: lower-alpha }
ol.upperalpha {
list-style: upper-alpha }
ol.lowerroman {
list-style: lower-roman }
ol.upperroman {
list-style: upper-roman }
p.attribution {
text-align: right ;
margin-left: 50% }
p.caption {
font-style: italic }
p.credits {
font-style: italic ;
font-size: smaller }
p.label {
white-space: nowrap }
p.rubric {
font-weight: bold ;
font-size: larger ;
color: maroon ;
text-align: center }
p.sidebar-title {
font-family: sans-serif ;
font-weight: bold ;
font-size: larger }
p.sidebar-subtitle {
font-family: sans-serif ;
font-weight: bold }
p.topic-title {
font-weight: bold }
pre.address {
margin-bottom: 0 ;
margin-top: 0 ;
font: inherit }
pre.literal-block, pre.doctest-block, pre.math, pre.code {
margin-left: 2em ;
margin-right: 2em }
pre.code .ln { color: gray; } /* line numbers */
pre.code, code { background-color: #eeeeee }
pre.code .comment, code .comment { color: #5C6576 }
pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold }
pre.code .literal.string, code .literal.string { color: #0C5404 }
pre.code .name.builtin, code .name.builtin { color: #352B84 }
pre.code .deleted, code .deleted { background-color: #DEB0A1}
pre.code .inserted, code .inserted { background-color: #A3D289}
span.classifier {
font-family: sans-serif ;
font-style: oblique }
span.classifier-delimiter {
font-family: sans-serif ;
font-weight: bold }
span.interpreted {
font-family: sans-serif }
span.option {
white-space: nowrap }
span.pre {
white-space: pre }
span.problematic, pre.problematic {
color: red }
span.section-subtitle {
/* font-size relative to parent (h1..h6 element) */
font-size: 80% }
table.citation {
border-left: solid 1px gray;
margin-left: 1px }
table.docinfo {
margin: 2em 4em }
table.docutils {
margin-top: 0.5em ;
margin-bottom: 0.5em }
table.footnote {
border-left: solid 1px black;
margin-left: 1px }
table.docutils td, table.docutils th,
table.docinfo td, table.docinfo th {
padding-left: 0.5em ;
padding-right: 0.5em ;
vertical-align: top }
table.docutils th.field-name, table.docinfo th.docinfo-name {
font-weight: bold ;
text-align: left ;
white-space: nowrap ;
padding-left: 0 }
/* "booktabs" style (no vertical lines) */
table.docutils.booktabs {
border: 0px;
border-top: 2px solid;
border-bottom: 2px solid;
border-collapse: collapse;
}
table.docutils.booktabs * {
border: 0px;
}
table.docutils.booktabs th {
border-bottom: thin solid;
text-align: left;
}
h1 tt.docutils, h2 tt.docutils, h3 tt.docutils,
h4 tt.docutils, h5 tt.docutils, h6 tt.docutils {
font-size: 100% }
ul.auto-toc {
list-style-type: none }
</style>
</head>
<body>
<div class="document" id="sale-order-batch-stock-binding">
<h1 class="title">Sale Order Batch Stock Binding</h1>
<!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:c32b4440b58e98f3bea5539d78dfa2916d64aebc757c140d78b67b2a2ce288da
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
<p><a class="reference external image-reference" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external image-reference" href="http://www.gnu.org/licenses/lgpl-3.0-standalone.html"><img alt="License: LGPL-3" src="https://img.shields.io/badge/licence-LGPL--3-blue.png" /></a> <a class="reference external image-reference" href="https://github.com/bakeupboys/nfu/tree/16.0/sale_order_batch_stock"><img alt="bakeupboys/nfu" src="https://img.shields.io/badge/github-bakeupboys%2Fnfu-lightgray.png?logo=github" /></a></p>
<p>This module adds Deliverys to Sale ORder Batch Views.</p>
<p><strong>Table of contents</strong></p>
<div class="contents local topic" id="contents">
<ul class="simple">
<li><a class="reference internal" href="#changelog" id="toc-entry-1">Changelog</a></li>
<li><a class="reference internal" href="#bug-tracker" id="toc-entry-2">Bug Tracker</a></li>
<li><a class="reference internal" href="#credits" id="toc-entry-3">Credits</a><ul>
<li><a class="reference internal" href="#authors" id="toc-entry-4">Authors</a></li>
<li><a class="reference internal" href="#contributors" id="toc-entry-5">Contributors</a></li>
<li><a class="reference internal" href="#maintainers" id="toc-entry-6">Maintainers</a></li>
</ul>
</li>
</ul>
</div>
<div class="section" id="changelog">
<h1><a class="toc-backref" href="#toc-entry-1">Changelog</a></h1>
<table class="docutils field-list" frame="void" rules="none">
<col class="field-name" />
<col class="field-body" />
<tbody valign="top">
<tr class="field"><th class="field-name">0.0.1:</th><td class="field-body">Initial module.</td>
</tr>
</tbody>
</table>
</div>
<div class="section" id="bug-tracker">
<h1><a class="toc-backref" href="#toc-entry-2">Bug Tracker</a></h1>
<p>Bugs are tracked on <a class="reference external" href="https://github.com/bakeupboys/nfu/issues">GitHub Issues</a>.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us to smash it by providing a detailed and welcomed
<a class="reference external" href="https://github.com/bakeupboys/nfu/issues/new?body=module:%20sale_order_batch_stock%0Aversion:%2016.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**">feedback</a>.</p>
<p>Do not contact contributors directly about support or help with technical issues.</p>
</div>
<div class="section" id="credits">
<h1><a class="toc-backref" href="#toc-entry-3">Credits</a></h1>
<div class="section" id="authors">
<h2><a class="toc-backref" href="#toc-entry-4">Authors</a></h2>
<ul class="simple">
<li>BAKEUP</li>
</ul>
</div>
<div class="section" id="contributors">
<h2><a class="toc-backref" href="#toc-entry-5">Contributors</a></h2>
<ul class="simple">
<li>Niels Göttsch &lt;<a class="reference external" href="mailto:niels&#64;ziemlichoptimal.de">niels&#64;ziemlichoptimal.de</a>&gt;</li>
</ul>
</div>
<div class="section" id="maintainers">
<h2><a class="toc-backref" href="#toc-entry-6">Maintainers</a></h2>
<p>This module is part of the <a class="reference external" href="https://github.com/bakeupboys/nfu/tree/16.0/sale_order_batch_stock">bakeupboys/nfu</a> project on GitHub.</p>
<p>You are welcome to contribute.</p>
</div>
</div>
</div>
</body>
</html>

View file

@ -1,22 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<record id="view_order_batch_form" model="ir.ui.view">
<field name="name">sale.order.batch.form.stock</field>
<field name="model">sale.order.batch</field>
<field name="inherit_id" ref="sale_order_batch.view_order_batch_form"/>
<field name="arch" type="xml">
<xpath expr="//button[@name='action_view_invoice']" position="before">
<button
type="object"
name="action_view_delivery"
class="oe_stat_button"
icon="fa-truck"
attrs="{'invisible': [('delivery_count', '=', 0)]}"
groups="stock.group_stock_user"
>
<field name="delivery_count" widget="statinfo" string="Delivery"/>
</button>
</xpath>
</field>
</record>
</odoo>

View file

@ -1 +0,0 @@
../../../../sale_order_batch

View file

@ -1,6 +0,0 @@
import setuptools
setuptools.setup(
setup_requires=['setuptools-odoo'],
odoo_addon=True,
)

View file

@ -1 +0,0 @@
../../../../sale_order_batch_stock

View file

@ -1,6 +0,0 @@
import setuptools
setuptools.setup(
setup_requires=['setuptools-odoo'],
odoo_addon=True,
)