[IMP] "website_sale" compatible for multi website
This commit is contained in:
parent
d00801d466
commit
23e5ea0865
@ -12,8 +12,10 @@ from flectra.http import request
|
||||
class WebsiteSaleBackend(WebsiteBackend):
|
||||
|
||||
@http.route()
|
||||
def fetch_dashboard_data(self, date_from, date_to):
|
||||
results = super(WebsiteSaleBackend, self).fetch_dashboard_data(date_from, date_to)
|
||||
def fetch_dashboard_data(self, date_from, date_to, website_id=None):
|
||||
if not website_id:
|
||||
website_id = request.website.id
|
||||
results = super(WebsiteSaleBackend, self).fetch_dashboard_data(date_from, date_to, website_id)
|
||||
|
||||
sales_values = dict(
|
||||
graph=[],
|
||||
@ -44,6 +46,7 @@ class WebsiteSaleBackend(WebsiteBackend):
|
||||
('team_id.team_type', '=', 'website'),
|
||||
('state', 'in', ['sale', 'done']),
|
||||
('date', '>=', date_from),
|
||||
('website_id.id', '=', website_id),
|
||||
('date', '<=', date_to)],
|
||||
fields=['product_id', 'product_uom_qty', 'price_subtotal'],
|
||||
groupby='product_id', orderby='product_uom_qty desc', limit=5)
|
||||
@ -60,7 +63,8 @@ class WebsiteSaleBackend(WebsiteBackend):
|
||||
sale_order_domain = [
|
||||
('team_id', 'in', request.env['crm.team'].search([('team_type', '=', 'website')]).ids),
|
||||
('date_order', '>=', fields.Datetime.to_string(datetime_from)),
|
||||
('date_order', '<=', fields.Datetime.to_string(datetime_to))]
|
||||
('date_order', '<=', fields.Datetime.to_string(datetime_to)),
|
||||
('website_id', '=', website_id)]
|
||||
so_group_data = request.env['sale.order'].read_group(sale_order_domain, fields=['state'], groupby='state')
|
||||
for res in so_group_data:
|
||||
if res.get('state') == 'sent':
|
||||
@ -74,7 +78,8 @@ class WebsiteSaleBackend(WebsiteBackend):
|
||||
('team_id.team_type', '=', 'website'),
|
||||
('state', 'in', ['sale', 'done']),
|
||||
('date', '>=', date_from),
|
||||
('date', '<=', date_to)],
|
||||
('date', '<=', date_to),
|
||||
('website_id.id', '=', website_id)],
|
||||
fields=['team_id', 'price_subtotal'],
|
||||
groupby=['team_id'],
|
||||
)
|
||||
@ -114,7 +119,8 @@ class WebsiteSaleBackend(WebsiteBackend):
|
||||
('team_id.team_type', '=', 'website'),
|
||||
('state', 'in', ['sale', 'done']),
|
||||
('date', '>=', date_from),
|
||||
('date', '<=', date_to)
|
||||
('date', '<=', date_to),
|
||||
('website_id.id', '=', website_id)
|
||||
]
|
||||
sales_values['graph'] += [{
|
||||
'values': self._compute_sale_graph(date_date_from, date_date_to, sales_domain),
|
||||
|
@ -135,7 +135,7 @@ class WebsiteSale(http.Controller):
|
||||
def get_attribute_value_ids(self, product):
|
||||
""" list of selectable attributes of a product
|
||||
|
||||
:return: list of product variant description
|
||||
:return: list of product variant description as requested website
|
||||
(variant id, [visible attribute ids], variant price, variant sale price)
|
||||
"""
|
||||
# product attributes with at least two choices
|
||||
@ -185,6 +185,8 @@ class WebsiteSale(http.Controller):
|
||||
ids = [value[1]]
|
||||
if attrib:
|
||||
domain += [('attribute_line_ids.value_ids', 'in', ids)]
|
||||
if not request.env.user.has_group('website.group_website_publisher'):
|
||||
domain += [('website_ids', 'in', request.website.id)]
|
||||
|
||||
return domain
|
||||
|
||||
@ -222,11 +224,16 @@ class WebsiteSale(http.Controller):
|
||||
post["search"] = search
|
||||
if category:
|
||||
category = request.env['product.public.category'].browse(int(category))
|
||||
website = []
|
||||
for web in category.website_ids:
|
||||
website.append(web.id)
|
||||
if request.website.id not in website:
|
||||
return request.render('website.404')
|
||||
url = "/shop/category/%s" % slug(category)
|
||||
if attrib_list:
|
||||
post['attrib'] = attrib_list
|
||||
|
||||
categs = request.env['product.public.category'].search([('parent_id', '=', False)])
|
||||
categs = request.env['product.public.category'].search([('parent_id', '=', False), ('website_ids', 'in', request.website.id)])
|
||||
Product = request.env['product.template']
|
||||
|
||||
parent_category_ids = []
|
||||
@ -315,7 +322,7 @@ class WebsiteSale(http.Controller):
|
||||
|
||||
@http.route(['/shop/change_pricelist/<model("product.pricelist"):pl_id>'], type='http', auth="public", website=True)
|
||||
def pricelist_change(self, pl_id, **post):
|
||||
if (pl_id.selectable or pl_id == request.env.user.partner_id.property_product_pricelist) \
|
||||
if (pl_id.website_id or pl_id == request.env.user.partner_id.property_product_pricelist) \
|
||||
and request.website.is_pricelist_available(pl_id.id):
|
||||
request.session['website_sale_current_pl'] = pl_id.id
|
||||
request.website.sale_get_order(force_pricelist=pl_id.id)
|
||||
|
279
addons/website_sale/controllers/portal.py
Normal file
279
addons/website_sale/controllers/portal.py
Normal file
@ -0,0 +1,279 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Flectra. See LICENSE file for full copyright and licensing details.
|
||||
from flectra import http
|
||||
from flectra.http import request
|
||||
from flectra import tools, _
|
||||
from flectra.exceptions import AccessError
|
||||
from flectra.addons.sale.controllers.portal import CustomerPortal
|
||||
from flectra.addons.account.controllers.portal import PortalAccount
|
||||
|
||||
|
||||
class SaleCustomerPortal(CustomerPortal):
|
||||
|
||||
def _prepare_portal_layout_values(self):
|
||||
values = super(SaleCustomerPortal, self)._prepare_portal_layout_values()
|
||||
partner = request.env.user.partner_id
|
||||
|
||||
SaleOrder = request.env['sale.order']
|
||||
quotation_count = SaleOrder.search_count([
|
||||
('message_partner_ids', 'child_of',
|
||||
[partner.commercial_partner_id.id]),
|
||||
('state', 'in', ['sent', 'cancel']),
|
||||
('website_id', '=', request.website.id),
|
||||
])
|
||||
order_count = SaleOrder.search_count([
|
||||
('message_partner_ids', 'child_of',
|
||||
[partner.commercial_partner_id.id]),
|
||||
('state', 'in', ['sale', 'done']),
|
||||
('website_id', '=', request.website.id),
|
||||
])
|
||||
|
||||
values.update({
|
||||
'quotation_count': quotation_count,
|
||||
'order_count': order_count,
|
||||
})
|
||||
return values
|
||||
|
||||
@http.route(['/my/quotes', '/my/quotes/page/<int:page>'], type='http',
|
||||
auth="user", website=True)
|
||||
def portal_my_quotes(self, page=1, date_begin=None, date_end=None,
|
||||
sortby=None, **kw):
|
||||
values = self._prepare_portal_layout_values()
|
||||
partner = request.env.user.partner_id
|
||||
SaleOrder = request.env['sale.order']
|
||||
|
||||
domain = [
|
||||
('message_partner_ids', 'child_of',
|
||||
[partner.commercial_partner_id.id]),
|
||||
('state', 'in', ['sent', 'cancel']),
|
||||
('website_id', '=', request.website.id),
|
||||
]
|
||||
|
||||
searchbar_sortings = {
|
||||
'date': {'label': _('Order Date'), 'order': 'date_order desc'},
|
||||
'name': {'label': _('Reference'), 'order': 'name'},
|
||||
'stage': {'label': _('Stage'), 'order': 'state'},
|
||||
}
|
||||
|
||||
# default sortby order
|
||||
if not sortby:
|
||||
sortby = 'date'
|
||||
sort_order = searchbar_sortings[sortby]['order']
|
||||
|
||||
archive_groups = self._get_archive_groups('sale.order', domain)
|
||||
if date_begin and date_end:
|
||||
domain += [('create_date', '>', date_begin),
|
||||
('create_date', '<=', date_end)]
|
||||
|
||||
# count for pager
|
||||
quotation_count = SaleOrder.search_count(domain)
|
||||
# make pager
|
||||
pager = request.website.pager(
|
||||
url="/my/quotes",
|
||||
url_args={'date_begin': date_begin, 'date_end': date_end,
|
||||
'sortby': sortby},
|
||||
total=quotation_count,
|
||||
page=page,
|
||||
step=self._items_per_page
|
||||
)
|
||||
# search the count to display, according to the pager data
|
||||
quotations = SaleOrder.search(domain, order=sort_order,
|
||||
limit=self._items_per_page,
|
||||
offset=pager['offset'])
|
||||
request.session['my_quotes_history'] = quotations.ids[:100]
|
||||
values.update({
|
||||
'date': date_begin,
|
||||
'quotations': quotations.sudo(),
|
||||
'page_name': 'quote',
|
||||
'pager': pager,
|
||||
'archive_groups': archive_groups,
|
||||
'default_url': '/my/quotes',
|
||||
'searchbar_sortings': searchbar_sortings,
|
||||
'sortby': sortby,
|
||||
})
|
||||
return request.render("sale.portal_my_quotations",
|
||||
values)
|
||||
|
||||
@http.route(['/my/orders', '/my/orders/page/<int:page>'], type='http',
|
||||
auth="user", website=True)
|
||||
def portal_my_orders(self, page=1, date_begin=None, date_end=None,
|
||||
sortby=None, **kw):
|
||||
values = self._prepare_portal_layout_values()
|
||||
partner = request.env.user.partner_id
|
||||
SaleOrder = request.env['sale.order']
|
||||
|
||||
domain = [
|
||||
('message_partner_ids', 'child_of',
|
||||
[partner.commercial_partner_id.id]),
|
||||
('state', 'in', ['sale', 'done']),
|
||||
('website_id', '=', request.website.id),
|
||||
]
|
||||
|
||||
searchbar_sortings = {
|
||||
'date': {'label': _('Order Date'), 'order': 'date_order desc'},
|
||||
'name': {'label': _('Reference'), 'order': 'name'},
|
||||
'stage': {'label': _('Stage'), 'order': 'state'},
|
||||
}
|
||||
# default sortby order
|
||||
if not sortby:
|
||||
sortby = 'date'
|
||||
sort_order = searchbar_sortings[sortby]['order']
|
||||
|
||||
archive_groups = self._get_archive_groups('sale.order', domain)
|
||||
if date_begin and date_end:
|
||||
domain += [('create_date', '>', date_begin),
|
||||
('create_date', '<=', date_end)]
|
||||
|
||||
# count for pager
|
||||
order_count = SaleOrder.search_count(domain)
|
||||
# pager
|
||||
pager = request.website.pager(
|
||||
url="/my/orders",
|
||||
url_args={'date_begin': date_begin, 'date_end': date_end,
|
||||
'sortby': sortby},
|
||||
total=order_count,
|
||||
page=page,
|
||||
step=self._items_per_page
|
||||
)
|
||||
# content according to pager and archive selected
|
||||
orders = SaleOrder.search(domain, order=sort_order,
|
||||
limit=self._items_per_page,
|
||||
offset=pager['offset'])
|
||||
request.session['my_orders_history'] = orders.ids[:100]
|
||||
|
||||
values.update({
|
||||
'date': date_begin,
|
||||
'orders': orders.sudo(),
|
||||
'page_name': 'order',
|
||||
'pager': pager,
|
||||
'archive_groups': archive_groups,
|
||||
'default_url': '/my/orders',
|
||||
'searchbar_sortings': searchbar_sortings,
|
||||
'sortby': sortby,
|
||||
})
|
||||
return request.render("sale.portal_my_orders", values)
|
||||
|
||||
@http.route()
|
||||
def portal_order_page(self, order=None, access_token=None, **kw):
|
||||
order = request.env['sale.order'].browse([order])
|
||||
try:
|
||||
if order.website_id != request.website:
|
||||
raise AccessError('Invalid Order !!')
|
||||
except AccessError:
|
||||
return request.render("website.403")
|
||||
|
||||
return super(SaleCustomerPortal, self).portal_order_page(order.id,
|
||||
access_token, **kw)
|
||||
|
||||
@http.route()
|
||||
def portal_order_report(self, order_id, access_token=None, **kw):
|
||||
order = request.env['sale.order'].browse([order_id])
|
||||
try:
|
||||
if order.website_id != request.website:
|
||||
raise AccessError('Invalid Order !!')
|
||||
except AccessError:
|
||||
return request.render("website.403")
|
||||
|
||||
return super(SaleCustomerPortal, self).portal_order_report(order_id,
|
||||
access_token,
|
||||
**kw)
|
||||
|
||||
|
||||
class AccountCustomerPortal(PortalAccount):
|
||||
|
||||
def _prepare_portal_layout_values(self):
|
||||
values = super(AccountCustomerPortal, self)._prepare_portal_layout_values()
|
||||
partner = request.env.user.partner_id
|
||||
|
||||
invoice_count = request.env['account.invoice'].search_count([
|
||||
('type', 'in', ['out_invoice', 'out_refund']),
|
||||
('message_partner_ids', 'child_of',
|
||||
[partner.commercial_partner_id.id]),
|
||||
('state', 'in', ['open', 'paid', 'cancel']),
|
||||
('website_id', '=', request.website.id),
|
||||
])
|
||||
|
||||
values['invoice_count'] = invoice_count
|
||||
return values
|
||||
|
||||
@http.route()
|
||||
def portal_my_invoices(self, page=1, date_begin=None, date_end=None,
|
||||
sortby=None, **kw):
|
||||
values = self._prepare_portal_layout_values()
|
||||
partner = request.env.user.partner_id
|
||||
AccountInvoice = request.env['account.invoice']
|
||||
|
||||
domain = [
|
||||
('type', 'in', ['out_invoice', 'out_refund']),
|
||||
('message_partner_ids', 'child_of',
|
||||
[partner.commercial_partner_id.id]),
|
||||
('state', 'in', ['open', 'paid', 'cancelled']),
|
||||
('website_id', '=', request.website.id),
|
||||
]
|
||||
|
||||
searchbar_sortings = {
|
||||
'date': {'label': _('Invoice Date'), 'order': 'date_invoice desc'},
|
||||
'duedate': {'label': _('Due Date'), 'order': 'date_due desc'},
|
||||
'name': {'label': _('Reference'), 'order': 'name desc'},
|
||||
'state': {'label': _('Status'), 'order': 'state'},
|
||||
}
|
||||
# default sort by order
|
||||
if not sortby:
|
||||
sortby = 'date'
|
||||
order = searchbar_sortings[sortby]['order']
|
||||
|
||||
archive_groups = self._get_archive_groups('account.invoice', domain)
|
||||
if date_begin and date_end:
|
||||
domain += [('create_date', '>', date_begin),
|
||||
('create_date', '<=', date_end)]
|
||||
|
||||
# count for pager
|
||||
invoice_count = AccountInvoice.search_count(domain)
|
||||
# pager
|
||||
pager = request.website.pager(
|
||||
url="/my/invoices",
|
||||
url_args={'date_begin': date_begin,
|
||||
'date_end': date_end,
|
||||
'sortby': sortby},
|
||||
total=invoice_count,
|
||||
page=page,
|
||||
step=self._items_per_page
|
||||
)
|
||||
# content according to pager and archive selected
|
||||
invoices = AccountInvoice.search(domain, order=order,
|
||||
limit=self._items_per_page,
|
||||
offset=pager['offset'])
|
||||
values.update({
|
||||
'date': date_begin,
|
||||
'invoices': invoices,
|
||||
'page_name': 'invoice',
|
||||
'pager': pager,
|
||||
'archive_groups': archive_groups,
|
||||
'default_url': '/my/invoices',
|
||||
'searchbar_sortings': searchbar_sortings,
|
||||
'sortby': sortby,
|
||||
})
|
||||
return request.render("account.portal_my_invoices", values)
|
||||
|
||||
@http.route()
|
||||
def portal_my_invoice_detail(self, invoice_id, access_token=None, **kw):
|
||||
invoice = request.env['account.invoice'].browse(invoice_id)
|
||||
try:
|
||||
if invoice.website_id != request.website:
|
||||
raise AccessError('Invalid Invoice !!')
|
||||
except AccessError:
|
||||
return request.render("website.403")
|
||||
|
||||
return super(PortalAccount, self).portal_my_invoice_detail(invoice_id, access_token,**kw)
|
||||
|
||||
@http.route()
|
||||
def portal_my_invoice_report(self, invoice_id, **kw):
|
||||
invoice = request.env['account.invoice'].browse(invoice_id)
|
||||
try:
|
||||
if invoice.website_id != request.website:
|
||||
raise AccessError('Invalid Invoice !!')
|
||||
except AccessError:
|
||||
return request.render("website.403")
|
||||
|
||||
return super(PortalAccount, self).portal_my_invoices_report(invoice_id,
|
||||
**kw)
|
@ -38,10 +38,6 @@
|
||||
<field name="salesteam_id" ref="sales_team.salesteam_website_sales"/>
|
||||
</record>
|
||||
|
||||
<record model="product.pricelist" id="product.list0">
|
||||
<field name="selectable" eval="False" />
|
||||
<field name="website_id" ref="website.default_website" />
|
||||
</record>
|
||||
</data>
|
||||
<data>
|
||||
<record id="sale.model_sale_order" model="ir.model">
|
||||
|
@ -713,8 +713,7 @@ Weight: 31 grams</field>
|
||||
|
||||
<record model="product.pricelist" id="product.list0">
|
||||
<field name="name">USD</field>
|
||||
<field name="selectable" eval="True" />
|
||||
<field name="website_id" ref="website.default_website" />
|
||||
<!--<field name="website_id" eval="[(6, 0, [ref('website.default_website').id])]"/>-->
|
||||
<field name="sequence">3</field>
|
||||
<field name="currency_id" ref="base.USD"/>
|
||||
</record>
|
||||
@ -727,8 +726,7 @@ Weight: 31 grams</field>
|
||||
|
||||
<record id="list_christmas" model="product.pricelist">
|
||||
<field name="name">Christmas</field>
|
||||
<field name="selectable" eval="False" />
|
||||
<field name="website_id" ref="website.default_website" />
|
||||
<!--<field name="website_id" eval="[(6, 0, [ref('website.default_website').id])]"/>-->
|
||||
<field name="country_group_ids" eval="[(6,0,[ref('base.europe')])]" />
|
||||
<field name="sequence">20</field>
|
||||
</record>
|
||||
@ -741,8 +739,7 @@ Weight: 31 grams</field>
|
||||
|
||||
<record id="list_benelux" model="product.pricelist">
|
||||
<field name="name">Benelux</field>
|
||||
<field name="selectable" eval="False" />
|
||||
<field name="website_id" ref="website.default_website" />
|
||||
<!--<field name="website_id" eval="[(6, 0, [ref('website.default_website').id])]"/>-->
|
||||
<field name="country_group_ids" eval="[(6,0,[ref('benelux')])]" />
|
||||
<field name="sequence">2</field>
|
||||
</record>
|
||||
@ -757,8 +754,7 @@ Weight: 31 grams</field>
|
||||
|
||||
<record id="list_europe" model="product.pricelist">
|
||||
<field name="name">EUR</field>
|
||||
<field name="selectable" eval="True" />
|
||||
<field name="website_id" ref="website.default_website" />
|
||||
<!--<field name="website_id" eval="[(6, 0, [ref('website.default_website').id])]"/>-->
|
||||
<field name="country_group_ids" eval="[(6,0,[ref('base.europe')])]" />
|
||||
<field name="sequence">3</field>
|
||||
<field name="currency_id" ref="base.EUR"/>
|
||||
|
@ -1,6 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo, Flectra. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import account_invoice
|
||||
from . import crm_team
|
||||
from . import ir_http
|
||||
from . import mail_compose_message
|
||||
@ -8,6 +9,7 @@ from . import product
|
||||
from . import res_country
|
||||
from . import res_partner
|
||||
from . import sale_order
|
||||
from . import sale_report
|
||||
from . import ir_model_fields
|
||||
from . import website
|
||||
from . import res_config_settings
|
||||
|
24
addons/website_sale/models/account_invoice.py
Normal file
24
addons/website_sale/models/account_invoice.py
Normal file
@ -0,0 +1,24 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Flectra. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from flectra import api, fields, models, _
|
||||
|
||||
|
||||
class AccountInvoice(models.Model):
|
||||
_inherit = 'account.invoice'
|
||||
|
||||
website_id = fields.Many2one('website', string="Website")
|
||||
|
||||
|
||||
class SaleAdvancePaymentInv(models.TransientModel):
|
||||
_inherit = "sale.advance.payment.inv"
|
||||
|
||||
@api.multi
|
||||
def _create_invoice(self, order, so_line, amount):
|
||||
res = super(SaleAdvancePaymentInv, self)._create_invoice(order,
|
||||
so_line,
|
||||
amount)
|
||||
|
||||
res.website_id = order.website_id
|
||||
|
||||
return res
|
@ -15,15 +15,23 @@ class ProductStyle(models.Model):
|
||||
html_class = fields.Char(string='HTML Classes')
|
||||
|
||||
|
||||
class WebsitePriceList(models.Model):
|
||||
_name = "website.product.pricelist"
|
||||
|
||||
pricelist_id = fields.Many2one('product.pricelist',
|
||||
string='Website Pricelist')
|
||||
website_id = fields.Many2one('website', string='Website')
|
||||
selectable = fields.Boolean(string="selectable", default=True)
|
||||
|
||||
|
||||
class ProductPricelist(models.Model):
|
||||
_inherit = "product.pricelist"
|
||||
|
||||
def _default_website(self):
|
||||
return self.env['website'].search([], limit=1)
|
||||
|
||||
website_id = fields.Many2one('website', string="website", default=_default_website)
|
||||
website_id = fields.One2many('website.product.pricelist', 'pricelist_id', string='Website')
|
||||
code = fields.Char(string='E-commerce Promotional Code', groups="base.group_user")
|
||||
selectable = fields.Boolean(help="Allow the end user to choose this price list")
|
||||
|
||||
def clear_cache(self):
|
||||
# website._get_pl() is cached to avoid to recompute at each request the
|
||||
@ -75,16 +83,40 @@ class ProductPublicCategory(models.Model):
|
||||
help="Small-sized image of the category. It is automatically "
|
||||
"resized as a 64x64px image, with aspect ratio preserved. "
|
||||
"Use this field anywhere a small image is required.")
|
||||
website_ids = fields.Many2many('website', 'website_prod_public_categ_rel',
|
||||
'website_id', 'category_id',
|
||||
string='Websites', copy=False,
|
||||
help='List of websites in which '
|
||||
'category is published.')
|
||||
|
||||
@api.model
|
||||
def create(self, vals):
|
||||
tools.image_resize_images(vals)
|
||||
return super(ProductPublicCategory, self).create(vals)
|
||||
res = super(ProductPublicCategory, self).create(vals)
|
||||
# @todo Check different test-case: child & parent category
|
||||
if res.parent_id:
|
||||
res.parent_id.write({
|
||||
'website_ids': [(4, website_id.id) for website_id in res.website_ids]
|
||||
})
|
||||
return res
|
||||
|
||||
@api.multi
|
||||
def write(self, vals):
|
||||
tools.image_resize_images(vals)
|
||||
return super(ProductPublicCategory, self).write(vals)
|
||||
res = super(ProductPublicCategory, self).write(vals)
|
||||
# @todo Check different test-case: child & parent category
|
||||
if self.parent_id and self.website_ids.ids:
|
||||
self.parent_id.write({
|
||||
'website_ids': [(4, website_id.id) for website_id in self.website_ids]
|
||||
})
|
||||
if self.child_id:
|
||||
for child_id in self.child_id:
|
||||
for website_id in child_id.website_ids:
|
||||
if website_id not in self.website_ids:
|
||||
child_id.write({
|
||||
'website_ids': [(3, website_id.id)]
|
||||
})
|
||||
return res
|
||||
|
||||
@api.constrains('parent_id')
|
||||
def check_parent_id(self):
|
||||
@ -132,6 +164,11 @@ class ProductTemplate(models.Model):
|
||||
website_price = fields.Float('Website price', compute='_website_price', digits=dp.get_precision('Product Price'))
|
||||
website_public_price = fields.Float('Website public price', compute='_website_price', digits=dp.get_precision('Product Price'))
|
||||
website_price_difference = fields.Boolean('Website price difference', compute='_website_price')
|
||||
website_ids = fields.Many2many('website', 'website_prod_pub_rel',
|
||||
'website_id', 'product_id',
|
||||
string='Websites', copy=False,
|
||||
help='List of websites in which '
|
||||
'Product is published.')
|
||||
|
||||
def _website_price(self):
|
||||
# First filter out the ones that have no variant:
|
||||
@ -180,6 +217,11 @@ class ProductTemplate(models.Model):
|
||||
class Product(models.Model):
|
||||
_inherit = "product.product"
|
||||
|
||||
def _get_default_website_ids(self):
|
||||
default_website_id = self.env.ref('website.default_website')
|
||||
return [default_website_id.id] if default_website_id else None
|
||||
|
||||
website_ids = fields.Many2many('website', string="Publish Product On Website", default=_get_default_website_ids)
|
||||
website_price = fields.Float('Website price', compute='_website_price', digits=dp.get_precision('Product Price'))
|
||||
website_public_price = fields.Float('Website public price', compute='_website_price', digits=dp.get_precision('Product Price'))
|
||||
website_price_difference = fields.Boolean('Website price difference', compute='_website_price')
|
||||
|
@ -21,6 +21,8 @@ class SaleOrder(models.Model):
|
||||
string='Order Lines displayed on Website', readonly=True,
|
||||
help='Order Lines to be displayed on the website. They should not be used for computation purpose.',
|
||||
)
|
||||
website_id = fields.Many2one('website', string='Website',
|
||||
help='Website reference for quotation/order.')
|
||||
cart_quantity = fields.Integer(compute='_compute_cart_info', string='Cart Quantity')
|
||||
only_services = fields.Boolean(compute='_compute_cart_info', string='Only Services')
|
||||
can_directly_mark_as_paid = fields.Boolean(compute='_compute_can_directly_mark_as_paid',
|
||||
@ -256,3 +258,9 @@ class SaleOrder(models.Model):
|
||||
self.payment_tx_id.state = 'done'
|
||||
else:
|
||||
raise ValidationError(_("The quote should be sent and the payment acquirer type should be manual or wire transfer"))
|
||||
|
||||
@api.multi
|
||||
def _prepare_invoice(self):
|
||||
res = super(SaleOrder, self)._prepare_invoice()
|
||||
res['website_id'] = self.website_id.id
|
||||
return res
|
||||
|
49
addons/website_sale/models/sale_report.py
Normal file
49
addons/website_sale/models/sale_report.py
Normal file
@ -0,0 +1,49 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of flectra. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from flectra import api, fields, models
|
||||
|
||||
|
||||
class SaleReport(models.Model):
|
||||
_inherit = "sale.report"
|
||||
website_id = fields.Many2one('website', 'Website', readonly=True)
|
||||
|
||||
def _select(self):
|
||||
select_str = """
|
||||
WITH currency_rate as (%s)
|
||||
SELECT min(l.id) as id,
|
||||
l.product_id as product_id,
|
||||
t.uom_id as product_uom,
|
||||
sum(l.product_uom_qty / u.factor * u2.factor) as product_uom_qty,
|
||||
sum(l.qty_delivered / u.factor * u2.factor) as qty_delivered,
|
||||
sum(l.qty_invoiced / u.factor * u2.factor) as qty_invoiced,
|
||||
sum(l.qty_to_invoice / u.factor * u2.factor) as qty_to_invoice,
|
||||
sum(l.price_total / COALESCE(cr.rate, 1.0)) as price_total,
|
||||
sum(l.price_subtotal / COALESCE(cr.rate, 1.0)) as price_subtotal,
|
||||
sum(l.amt_to_invoice / COALESCE(cr.rate, 1.0)) as amt_to_invoice,
|
||||
sum(l.amt_invoiced / COALESCE(cr.rate, 1.0)) as amt_invoiced,
|
||||
count(*) as nbr,
|
||||
s.name as name,
|
||||
s.date_order as date,
|
||||
s.confirmation_date as confirmation_date,
|
||||
s.state as state,
|
||||
s.partner_id as partner_id,
|
||||
s.user_id as user_id,
|
||||
s.company_id as company_id,
|
||||
extract(epoch from avg(date_trunc('day',s.date_order)-date_trunc('day',s.create_date)))/(24*60*60)::decimal(16,2) as delay,
|
||||
t.categ_id as categ_id,
|
||||
s.pricelist_id as pricelist_id,
|
||||
s.analytic_account_id as analytic_account_id,
|
||||
s.team_id as team_id,
|
||||
s.website_id as website_id,
|
||||
p.product_tmpl_id,
|
||||
partner.country_id as country_id,
|
||||
partner.commercial_partner_id as commercial_partner_id,
|
||||
sum(p.weight * l.product_uom_qty / u.factor * u2.factor) as weight,
|
||||
sum(p.volume * l.product_uom_qty / u.factor * u2.factor) as volume
|
||||
""" % self.env['res.currency']._select_companies_rates()
|
||||
return select_str
|
||||
|
||||
def _group_by(self):
|
||||
res = super(SaleReport, self)._group_by()
|
||||
return res + ',s.website_id'
|
@ -17,12 +17,14 @@ class Website(models.Model):
|
||||
currency_id = fields.Many2one('res.currency', related='pricelist_id.currency_id', string='Default Currency')
|
||||
salesperson_id = fields.Many2one('res.users', string='Salesperson')
|
||||
salesteam_id = fields.Many2one('crm.team', string='Sales Channel')
|
||||
pricelist_ids = fields.One2many('product.pricelist', compute="_compute_pricelist_ids",
|
||||
pricelist_ids = fields.One2many('website.product.pricelist', compute="_compute_pricelist_ids",
|
||||
string='Price list available for this Ecommerce/Website')
|
||||
|
||||
@api.one
|
||||
def _compute_pricelist_ids(self):
|
||||
self.pricelist_ids = self.env["product.pricelist"].search([("website_id", "=", self.id)])
|
||||
for website in self:
|
||||
website.pricelist_ids = website.env["website.product.pricelist"].search([("website_id", "=", website.id)])
|
||||
# self.pricelist_ids = self.env["product.pricelist"].search([("website_id", "=", self.id)])
|
||||
|
||||
@api.multi
|
||||
def _compute_pricelist_id(self):
|
||||
@ -32,8 +34,8 @@ class Website(models.Model):
|
||||
website.pricelist_id = website.get_current_pricelist()
|
||||
|
||||
# This method is cached, must not return records! See also #8795
|
||||
@tools.ormcache('self.env.uid', 'country_code', 'show_visible', 'website_pl', 'current_pl', 'all_pl', 'partner_pl', 'order_pl')
|
||||
def _get_pl_partner_order(self, country_code, show_visible, website_pl, current_pl, all_pl, partner_pl=False, order_pl=False):
|
||||
@tools.ormcache('self.env.uid', 'country_code', 'show_visible', 'website_pl', 'current_pl', 'all_pls', 'partner_pl', 'order_pl')
|
||||
def _get_pl_partner_order(self, country_code, show_visible, website_pl, current_pl, all_pls, partner_pl=False, order_pl=False):
|
||||
""" Return the list of pricelists that can be used on website for the current user.
|
||||
:param str country_code: code iso or False, If set, we search only price list available for this country
|
||||
:param bool show_visible: if True, we don't display pricelist where selectable is False (Eg: Code promo)
|
||||
@ -49,7 +51,7 @@ class Website(models.Model):
|
||||
if country_code:
|
||||
for cgroup in self.env['res.country.group'].search([('country_ids.code', '=', country_code)]):
|
||||
for group_pricelists in cgroup.pricelist_ids:
|
||||
if not show_visible or group_pricelists.selectable or group_pricelists.id in (current_pl, order_pl):
|
||||
if not show_visible or group_pricelists.website_id.filtered(lambda web_pl: web_pl.website_id == self.get_current_website() and web_pl.selectable) or group_pricelists.id in (current_pl, order_pl):
|
||||
pricelists |= group_pricelists
|
||||
|
||||
partner = self.env.user.partner_id
|
||||
@ -59,9 +61,11 @@ class Website(models.Model):
|
||||
pricelists |= partner.property_product_pricelist
|
||||
|
||||
if not pricelists: # no pricelist for this country, or no GeoIP
|
||||
pricelists |= all_pl.filtered(lambda pl: not show_visible or pl.selectable or pl.id in (current_pl, order_pl))
|
||||
for all_pl in all_pls:
|
||||
pricelists |= all_pl.pricelist_id.filtered(lambda pl: not show_visible or (all_pl.pricelist_id == pl and all_pl.selectable) or pl.id in (current_pl, order_pl))
|
||||
else:
|
||||
pricelists |= all_pl.filtered(lambda pl: not show_visible and pl.sudo().code)
|
||||
for all_pl in all_pls:
|
||||
pricelists |= all_pl.pricelist_id.filtered(lambda pl: not show_visible and pl.sudo().code)
|
||||
|
||||
# This method is cached, must not return records! See also #8795
|
||||
return pricelists.ids
|
||||
@ -167,6 +171,7 @@ class Website(models.Model):
|
||||
'partner_invoice_id': addr['invoice'],
|
||||
'partner_shipping_id': addr['delivery'],
|
||||
'user_id': salesperson_id or self.salesperson_id.id or default_user_id,
|
||||
'website_id': request.website.id,
|
||||
}
|
||||
company = self.company_id or pricelist.company_id
|
||||
if company:
|
||||
|
@ -19,3 +19,4 @@ access_account_tax_user,account.tax,account.model_account_tax,base.group_public,
|
||||
access_product_image_public,product.image public,model_product_image,,1,0,0,0
|
||||
access_product_image_publisher,product.image wbesite publisher,model_product_image,website.group_website_publisher,1,1,1,1
|
||||
access_product_image_sale,product.image sale,model_product_image,sales_team.group_sale_manager,1,1,1,1
|
||||
access_website_product_pricelist,access_website_product_pricelist,model_website_product_pricelist,,1,0,0,0
|
||||
|
|
@ -22,22 +22,30 @@ class TestWebsitePriceList(TransactionCase):
|
||||
self.env['product.pricelist'].search([]).write({'website_id': False})
|
||||
website_pls = ('list_benelux', 'list_christmas', 'list_europe')
|
||||
for pl in website_pls:
|
||||
self.env.ref('website_sale.' + pl).website_id = self.website.id
|
||||
self.env.ref('product.list0').website_id = self.website.id
|
||||
self.env.ref('website_sale.list_benelux').selectable = True
|
||||
self.env['website.product.pricelist'].create({
|
||||
'pricelist_id': self.env.ref('website_sale.' + pl).id,
|
||||
'website_id': self.website.id,
|
||||
'selectable': False if pl == 'list_christmas' else True
|
||||
})
|
||||
self.env['website.product.pricelist'].create({
|
||||
'pricelist_id': self.env.ref('product.list0').id,
|
||||
'website_id': self.website.id
|
||||
})
|
||||
self.website.pricelist_id = self.ref('product.list0')
|
||||
|
||||
ca_group = self.env['res.country.group'].create({
|
||||
'name': 'Canada',
|
||||
'country_ids': [(6, 0, [self.ref('base.ca')])]
|
||||
})
|
||||
self.env['product.pricelist'].create({
|
||||
ppl = self.env['product.pricelist'].create({
|
||||
'name': 'Canada',
|
||||
'selectable': True,
|
||||
'website_id': self.website.id,
|
||||
'country_group_ids': [(6, 0, [ca_group.id])],
|
||||
'sequence': 10
|
||||
})
|
||||
self.env['website.product.pricelist'].create({
|
||||
'pricelist_id': ppl.id,
|
||||
'website_id': self.website.id
|
||||
})
|
||||
patcher = patch('flectra.addons.website_sale.models.website.Website.get_pricelist_available', wraps=self._get_pricelist_available)
|
||||
patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
|
@ -21,4 +21,15 @@
|
||||
<field name="search_view_id" ref="product.product_pricelist_view_search" />
|
||||
<field name="context">{"default_base":'list_price'}</field>
|
||||
</record>
|
||||
|
||||
<record id="view_invoice_form_inherit" model="ir.ui.view">
|
||||
<field name="name">account.invoice.form.inherit</field>
|
||||
<field name="model">account.invoice</field>
|
||||
<field name="inherit_id" ref="account.invoice_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<field name='fiscal_position_id' position="after">
|
||||
<field name="website_id"/>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
</flectra>
|
@ -21,4 +21,24 @@
|
||||
<field name="view_id" ref="payment.transaction"/>
|
||||
<field name="help">You don't have any payments to capture from the website.</field>
|
||||
</record>
|
||||
|
||||
<record id="product_template_only_form_view_inherit" model="ir.ui.view">
|
||||
<field name="name">product.template.product.form.inherit</field>
|
||||
<field name="model">product.template</field>
|
||||
<field name="inherit_id" ref="product.product_template_only_form_view"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='attribute_line_ids']"
|
||||
position="after">
|
||||
<field name="product_variant_ids">
|
||||
<tree editable='bottom' delete="false" create="false">
|
||||
<field name = 'name' readonly="1"/>
|
||||
<field name = 'attribute_value_ids'
|
||||
widget="many2many_tags" readonly="1"/>
|
||||
<field name = 'lst_price' readonly="1"/>
|
||||
<field name = 'website_ids' widget="many2many_tags" />
|
||||
</tree>
|
||||
</field>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
</flectra>
|
||||
|
@ -74,6 +74,7 @@
|
||||
<field name="accessory_product_ids" widget="many2many_tags"/>
|
||||
<field name="website_style_ids" widget="many2many_tags" groups="base.group_no_one"/>
|
||||
</group>
|
||||
<field name="website_ids"/>
|
||||
</group>
|
||||
<button name="toggle_active" position="before">
|
||||
<button class="oe_stat_button" name="website_publish_button"
|
||||
@ -153,6 +154,7 @@
|
||||
<field name="name"/>
|
||||
<field name="parent_id"/>
|
||||
<field name="sequence"/>
|
||||
<field name="website_ids" widget="many2many_tags"/>
|
||||
</group>
|
||||
</div>
|
||||
</sheet>
|
||||
@ -193,20 +195,13 @@
|
||||
<field name="model">product.pricelist</field>
|
||||
<field name="arch" type="xml">
|
||||
<field name='currency_id' position='after'>
|
||||
<field name="website_id" string='Allow to use on '>
|
||||
<tree editable='bottom' string="Website Store">
|
||||
<field name="website_id"/>
|
||||
<field name="selectable"/>
|
||||
<field name="website_id" string='Allow to use on ' placeholder="None website" />
|
||||
</tree>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="website_sale_pricelist_tree_view" model="ir.ui.view">
|
||||
<field name="name">product.pricelist.tree.inherit.product</field>
|
||||
<field name="model">product.pricelist</field>
|
||||
<field name="inherit_id" ref="product.product_pricelist_view_tree"/>
|
||||
<field name="arch" type="xml">
|
||||
<field name="currency_id" position="after">
|
||||
<field name="selectable" />
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
|
@ -365,7 +365,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xs-12 col-md-6 o_setting_box" id="checkout_assignation_setting" groups="base.group_no_one">
|
||||
<div class="col-xs-12 col-md-6 o_setting_box" id="checkout_assignation_setting">
|
||||
<div class="o_setting_right_pane">
|
||||
<label string="Assignation"/>
|
||||
<div class="text-muted">
|
||||
@ -374,7 +374,7 @@
|
||||
<div class="content-group">
|
||||
<div class="row mt16">
|
||||
<label class="o_light_label col-md-3" string="Sales Channel" for="salesteam_id"/>
|
||||
<field name="salesteam_id" context="{'default_team_type': 'website'}"/>
|
||||
<field name="salesteam_id" context="{'default_team_type': 'website'}" required="True"/>
|
||||
</div>
|
||||
<div class="row">
|
||||
<label class="o_light_label col-md-3" for="salesperson_id"/>
|
||||
|
@ -11,6 +11,8 @@
|
||||
<filter string="From Website" name="from_website" domain="[('team_id.team_type', '=', 'website')]"/>
|
||||
<separator/>
|
||||
<filter string="Recovery Email to Send" name="recovery_email" domain="[('cart_recovery_email_sent', '=', False)]" />
|
||||
<separator/>
|
||||
<filter name="website_id" string="Website" context="{'group_by':'website_id'}"/>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
@ -35,6 +37,9 @@
|
||||
class="btn-primary"
|
||||
attrs="{'invisible': ['|', ('is_abandoned_cart', '=', False), ('cart_recovery_email_sent', '=', True)]}"/>
|
||||
</xpath>
|
||||
<xpath expr="//group[@name='sales_person']" position="inside">
|
||||
<field name="website_id"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
|
@ -193,7 +193,7 @@
|
||||
<t t-if="td_product">
|
||||
<t t-set="product" t-value="td_product['product']" />
|
||||
<td t-att-colspan="td_product['x'] != 1 and td_product['x']" t-att-rowspan="td_product['y'] != 1 and td_product['y']" t-attf-class="oe_product oe_grid oe-height-#{td_product['y']*2} #{ td_product['class'] }">
|
||||
<div class="oe_product_cart" t-att-data-publish="product.website_published and 'on' or 'off'">
|
||||
<div class="oe_product_cart" t-att-data-publish="website in product.website_ids and 'on' or 'off'">
|
||||
<t t-set="product_image_big" t-value="td_product['x']+td_product['y'] > 2" />
|
||||
<t t-call="website_sale.products_item" />
|
||||
</div>
|
||||
@ -258,7 +258,7 @@
|
||||
|
||||
<!-- Add to cart button-->
|
||||
<template id="categories_recursive" name="Category list">
|
||||
<li t-att-class="'active' if c.id == int(category or 0) else None">
|
||||
<li t-att-class="'active' if c.id == int(category or 0) else None" t-if="website in c.website_ids">
|
||||
<a t-att-href="keep('/shop/category/' + slug(c), category=0)" t-field="c.name"></a>
|
||||
<ul t-if="c.child_id" class="nav nav-pills nav-stacked nav-hierarchy">
|
||||
<t t-foreach="c.child_id" t-as="c">
|
||||
@ -365,7 +365,7 @@
|
||||
<template id="products_list_view" inherit_id="website_sale.products" active="False" customize_show="True" name="List View">
|
||||
<xpath expr="//div[@id='products_grid']//table" position="replace">
|
||||
<t t-foreach="products" t-as="product">
|
||||
<div class="oe_product oe_list oe_product_cart" t-att-data-publish="product.website_published and 'on' or 'off'">
|
||||
<div class="oe_product oe_list oe_product_cart" t-att-data-publish="website in product.website_ids and 'on' or 'off'">
|
||||
<t t-call="website_sale.products_item">
|
||||
<t t-set="show_publish" t-value="True" />
|
||||
</t>
|
||||
|
Loading…
Reference in New Issue
Block a user