Browse Source

[IMP] contract_publication. New module to manage publications and distribution.

pull/277/head
Ronald Portier 6 years ago
parent
commit
36da699f92
  1. 66
      contract_publication/README.rst
  2. 3
      contract_publication/__init__.py
  3. 25
      contract_publication/__manifest__.py
  4. 279
      contract_publication/i18n/contract_publication.pot
  5. 280
      contract_publication/i18n/nl.po
  6. 6
      contract_publication/models/__init__.py
  7. 33
      contract_publication/models/account_analytic_invoice_line.py
  8. 42
      contract_publication/models/product_template.py
  9. 180
      contract_publication/models/publication_distribution_list.py
  10. 18
      contract_publication/models/res_partner.py
  11. 3
      contract_publication/security/ir.model.access.csv
  12. 3
      contract_publication/tests/__init__.py
  13. 163
      contract_publication/tests/test_publication.py
  14. 98
      contract_publication/views/distribution_list.xml
  15. 13
      contract_publication/views/menu.xml
  16. 75
      contract_publication/views/product_template.xml
  17. 73
      contract_publication/views/res_partner.xml

66
contract_publication/README.rst

@ -0,0 +1,66 @@
.. image:: https://img.shields.io/badge/license-AGPL--3-blue.png
:target: https://www.gnu.org/licenses/agpl
:alt: License: AGPL-3
====================
Contract Publication
====================
This module allows to register publications that a partner can receive.
Publications can be of multiple kind: printed, electronic,
and free or paid for.
Paid publications can be linked to a contract (recurring analytic account).
For printed publications, you can setup a distribution list, in order that
one partner can have a subscription contract, but other partners actually
receive the publication. Think about organisations subscribing to
a publication and having them send out to their members.
Usage
=====
To use this module, you need to add publications as products. Then you can
have contract lines for those publications. In the partner form you will be
able to see the publications the partner has subscribed to and the partners
actually receiving the publications.
.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas
:alt: Try me on Runbot
:target: https://runbot.odoo-community.org/runbot/110/10.0
Bug Tracker
===========
Bugs are tracked on `GitHub Issues
<https://github.com/OCA/crm/issues>`_. In case of trouble, please
check there if your issue has already been reported. If you spotted it first,
help us smash it by providing detailed and welcomed feedback.
Images
------
* Odoo Community Association: `Icon <https://odoo-community.org/logo.png>`_.
Contributors
------------
* Ronald Portier <ronald@therp.nl> (https://therp.nl)
Do not contact contributors directly about support or help with technical issues.
Maintainer
----------
.. image:: https://odoo-community.org/logo.png
:alt: Odoo Community Association
:target: https://odoo-community.org
This module is maintained by the OCA.
OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.
To contribute to this module, please visit https://odoo-community.org.

3
contract_publication/__init__.py

@ -0,0 +1,3 @@
#-*- coding: utf-8 -*-
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from . import models

25
contract_publication/__manifest__.py

@ -0,0 +1,25 @@
# -*- coding: utf-8 -*-
# Copyright 2014-2019 Therp BV <https://therp.nl>.
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
{
'name': 'Publication',
'version': '10.0.1.0.0',
'category': 'Contract Management',
'author': 'Therp BV, '
'Odoo Community Association (OCA)',
'website': 'https://github.com/oca/contract',
'license': 'AGPL-3',
'summary': 'Maintain electronic or print publications for your relations.',
'depends': [
'contract',
],
'data': [
'views/product_template.xml',
'views/distribution_list.xml',
'views/res_partner.xml',
'views/menu.xml',
'security/ir.model.access.csv',
],
'auto_install': False,
'installable': True,
}

279
contract_publication/i18n/contract_publication.pot

@ -0,0 +1,279 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * contract_publication
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 10.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-12-01 10:39+0000\n"
"PO-Revision-Date: 2017-12-01 10:39+0000\n"
"Last-Translator: <>\n"
"Language-Team: \n"
"Language: en\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
#. module: contract_publication
#: model:ir.model.fields,field_description:publication.field_distribution_list_active
msgid "Active"
msgstr ""
#. module: contract_publication
#: model:ir.model.fields,help:publication.field_product_product_publishing_frequency_type
#: model:ir.model.fields,help:publication.field_product_template_publishing_frequency_type
msgid "At what intervals publication is published"
msgstr ""
#. module: contract_publication
#: model:ir.model.fields,field_description:publication.field_distribution_list_contract_partner_id
#: model:ir.ui.view,arch_db:publication.subscription_search
msgid "Contract Partner"
msgstr ""
#. module: contract_publication
#: model:ir.model.fields,field_description:publication.field_distribution_list_create_uid
msgid "Created by"
msgstr ""
#. module: contract_publication
#: model:ir.model.fields,field_description:publication.field_distribution_list_create_date
msgid "Created on"
msgstr ""
#. module: contract_publication
#: model:ir.model.fields,field_description:publication.field_distribution_list_date_end
msgid "Date end"
msgstr ""
#. module: contract_publication
#: model:ir.model.fields,field_description:publication.field_distribution_list_date_start
msgid "Date start"
msgstr ""
#. module: contract_publication
#: selection:product.template,publishing_frequency_type:0
msgid "Day(s)"
msgstr ""
#. module: contract_publication
#: model:ir.model.fields,field_description:publication.field_distribution_list_display_name
msgid "Display Name"
msgstr ""
#. module: contract_publication
#: model:ir.model.fields,field_description:publication.field_product_product_distribution_type
#: model:ir.model.fields,field_description:publication.field_product_template_distribution_type
msgid "Distribution"
msgstr ""
#. module: contract_publication
#: model:ir.actions.act_window,name:publication.action_distribution_list
#: model:ir.ui.menu,name:publication.menu_distribution_list
msgid "Distribution List"
msgstr ""
#. module: contract_publication
#: selection:product.template,distribution_type:0
msgid "Electronic"
msgstr ""
#. module: contract_publication
#: model:ir.model.fields,help:publication.field_distribution_list_date_end
msgid "End date is exclusive."
msgstr ""
#. module: contract_publication
#: model:ir.ui.view,arch_db:publication.subscription_search
msgid "Group By..."
msgstr ""
#. module: contract_publication
#: model:ir.model.fields,field_description:publication.field_distribution_list_id
msgid "ID"
msgstr ""
#. module: contract_publication
#: selection:product.template,publishing_frequency_type:0
msgid "Irregular"
msgstr ""
#. module: contract_publication
#: model:ir.model.fields,field_description:publication.field_distribution_list___last_update
msgid "Last Modified on"
msgstr ""
#. module: contract_publication
#: model:ir.model.fields,field_description:publication.field_distribution_list_write_uid
msgid "Last Updated by"
msgstr ""
#. module: contract_publication
#: model:ir.model.fields,field_description:publication.field_distribution_list_write_date
msgid "Last Updated on"
msgstr ""
#. module: contract_publication
#: selection:product.template,publishing_frequency_type:0
msgid "Month(s)"
msgstr ""
#. module: contract_publication
#: model:ir.model.fields,field_description:publication.field_distribution_list_name
msgid "Name"
msgstr ""
#. module: contract_publication
#: model:ir.model.fields,field_description:publication.field_distribution_list_assigned_count
msgid "Quantity already assigned"
msgstr ""
#. module: contract_publication
#: model:ir.model.fields,field_description:publication.field_distribution_list_copies
msgid "Number of copies"
msgstr ""
#. module: contract_publication
#: model:ir.model.fields,field_description:publication.field_distribution_list_available_count
msgid "Quantity still available"
msgstr ""
#. module: contract_publication
#: model:ir.model.fields,field_description:publication.field_distribution_list_contract_count
msgid "Quantity to distribute"
msgstr ""
#. module: contract_publication
#: model:ir.model,name:publication.model_res_partner
#: model:ir.model.fields,field_description:publication.field_account_analytic_contract_line_partner_id
#: model:ir.model.fields,field_description:publication.field_account_analytic_invoice_line_partner_id
msgid "Partner"
msgstr ""
#. module: contract_publication
#: selection:product.template,distribution_type:0
msgid "Print"
msgstr ""
#. module: contract_publication
#: model:ir.model,name:publication.model_product_template
msgid "Product Template"
msgstr ""
#. module: contract_publication
#: model:ir.model.fields,field_description:publication.field_product_product_publication
#: model:ir.model.fields,field_description:publication.field_product_template_publication
msgid "Product is a publication?"
msgstr ""
#. module: contract_publication
#: model:ir.model.fields,field_description:publication.field_distribution_list_product_id
#: model:ir.ui.view,arch_db:publication.product_template_only_form_view
#: model:ir.ui.view,arch_db:publication.subscription_search
msgid "Publication"
msgstr ""
#. module: contract_publication
#: model:ir.actions.act_window,name:publication.action_publication
#: model:ir.ui.menu,name:publication.menu_publication
msgid "Publications"
msgstr ""
#. module: contract_publication
#: model:ir.model.fields,field_description:publication.field_res_partner_distribution_list_ids
#: model:ir.model.fields,field_description:publication.field_res_users_distribution_list_ids
msgid "Publications received"
msgstr ""
#. module: contract_publication
#: model:ir.ui.view,arch_db:publication.view_partner_form
msgid "Publications to receive"
msgstr ""
#. module: contract_publication
#: model:ir.model.fields,field_description:publication.field_product_product_publishing_frequency_interval
#: model:ir.model.fields,field_description:publication.field_product_template_publishing_frequency_interval
msgid "Published Every"
msgstr ""
#. module: contract_publication
#: model:ir.model.fields,help:publication.field_product_product_publishing_frequency_interval
#: model:ir.model.fields,help:publication.field_product_template_publishing_frequency_interval
msgid "Published every (Days/Week/Month/Quarter/Year)"
msgstr ""
#. module: contract_publication
#: model:ir.model.fields,field_description:publication.field_product_product_publishing_frequency_type
#: model:ir.model.fields,field_description:publication.field_product_template_publishing_frequency_type
msgid "Publishing frequency"
msgstr ""
#. module: contract_publication
#: selection:product.template,publishing_frequency_type:0
msgid "Quarterly"
msgstr ""
#. module: contract_publication
#: model:ir.model.fields,field_description:publication.field_distribution_list_partner_id
#: model:ir.ui.view,arch_db:publication.subscription_search
msgid "Receiving Partner"
msgstr ""
#. module: contract_publication
#: model:ir.model.fields,field_description:publication.field_distribution_list_display_address
msgid "Receiving address"
msgstr ""
#. module: contract_publication
#: model:ir.model.fields,help:publication.field_product_product_distribution_type
#: model:ir.model.fields,help:publication.field_product_template_distribution_type
msgid "Required if product is publication"
msgstr ""
#. module: contract_publication
#: model:ir.ui.view,arch_db:publication.subscription_form
msgid "Subscription Address"
msgstr ""
#. module: contract_publication
#: model:ir.model.fields,field_description:publication.field_res_partner_subscription_ids
#: model:ir.model.fields,field_description:publication.field_res_users_subscription_ids
#: model:ir.ui.view,arch_db:publication.view_partner_form
msgid "Subscription contract lines"
msgstr ""
#. module: contract_publication
#: model:ir.model.fields,field_description:publication.field_account_analytic_contract_line_publication
#: model:ir.model.fields,field_description:publication.field_account_analytic_invoice_line_publication
msgid "Subscription product line"
msgstr ""
#. module: contract_publication
#: model:ir.ui.view,arch_db:publication.view_partner_form
#: model:ir.ui.view,arch_db:publication.subscription_search
#: model:ir.ui.view,arch_db:publication.subscription_tree
msgid "Subscriptions"
msgstr ""
#. module: contract_publication
#: selection:product.template,publishing_frequency_type:0
msgid "Week(s)"
msgstr ""
#. module: contract_publication
#: selection:product.template,publishing_frequency_type:0
msgid "Year(s)"
msgstr ""
#. module: contract_publication
#: model:ir.model,name:publication.model_account_analytic_invoice_line
msgid "account.analytic.invoice.line"
msgstr ""
#. module: contract_publication
#: model:ir.model,name:publication.model_distribution_list
msgid "distribution.list"
msgstr ""

280
contract_publication/i18n/nl.po

@ -0,0 +1,280 @@
# Translation of OpenERP Server.
# This file contains the translation of the following modules:
# * contract_publication
# Ronald Portier <ronald@therp.nl>, 2014, 2015.
#
msgid ""
msgstr ""
"Project-Id-Version: OpenERP Server 7.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-12-01 10:39+0000\n"
"PO-Revision-Date: 2015-02-09 12:54+0100\n"
"Last-Translator: Ronald Portier <ronald@therp.nl>\n"
"Language-Team: Nederlands <>\n"
"Language: nl_NL\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Gtranslator 2.91.6\n"
#. module: contract_publication
#: model:ir.model.fields,field_description:publication.field_distribution_list_active
msgid "Active"
msgstr "Actief"
#. module: contract_publication
#: model:ir.model.fields,help:publication.field_product_product_publishing_frequency_type
#: model:ir.model.fields,help:publication.field_product_template_publishing_frequency_type
msgid "At what intervals publication is published"
msgstr "Met wat voor frequentie de publicatie uitkomt"
#. module: contract_publication
#: model:ir.model.fields,field_description:publication.field_distribution_list_contract_partner_id
#: model:ir.ui.view,arch_db:publication.subscription_search
msgid "Contract Partner"
msgstr "Contract informatie"
#. module: contract_publication
#: model:ir.model.fields,field_description:publication.field_distribution_list_create_uid
msgid "Created by"
msgstr "Aangemaakt door"
#. module: contract_publication
#: model:ir.model.fields,field_description:publication.field_distribution_list_create_date
msgid "Created on"
msgstr "Aangemaakt op"
#. module: contract_publication
#: model:ir.model.fields,field_description:publication.field_distribution_list_date_end
msgid "Date end"
msgstr "Einddatum"
#. module: contract_publication
#: model:ir.model.fields,field_description:publication.field_distribution_list_date_start
msgid "Date start"
msgstr "Startdatum"
#. module: contract_publication
#: selection:product.template,publishing_frequency_type:0
msgid "Day(s)"
msgstr "Dagen"
#. module: contract_publication
#: model:ir.model.fields,field_description:publication.field_distribution_list_display_name
msgid "Display Name"
msgstr "Weergave naam"
#. module: contract_publication
#: model:ir.model.fields,field_description:publication.field_product_product_distribution_type
#: model:ir.model.fields,field_description:publication.field_product_template_distribution_type
msgid "Distribution"
msgstr "Voorkeur voor email of drukwerk"
#. module: contract_publication
#: model:ir.actions.act_window,name:publication.action_distribution_list
#: model:ir.ui.menu,name:publication.menu_distribution_list
msgid "Distribution List"
msgstr "Verzendlijst"
#. module: contract_publication
#: selection:product.template,distribution_type:0
msgid "Electronic"
msgstr "Elektronisch"
#. module: contract_publication
#: model:ir.model.fields,help:publication.field_distribution_list_date_end
msgid "End date is exclusive."
msgstr "Einddatum it tot datum"
#. module: contract_publication
#: model:ir.ui.view,arch_db:publication.subscription_search
msgid "Group By..."
msgstr "Groepeer op"
#. module: contract_publication
#: model:ir.model.fields,field_description:publication.field_distribution_list_id
msgid "ID"
msgstr "ID"
#. module: contract_publication
#: selection:product.template,publishing_frequency_type:0
msgid "Irregular"
msgstr "Onregelmatig"
#. module: contract_publication
#: model:ir.model.fields,field_description:publication.field_distribution_list___last_update
msgid "Last Modified on"
msgstr "Laatst gewijzigd op"
#. module: contract_publication
#: model:ir.model.fields,field_description:publication.field_distribution_list_write_uid
msgid "Last Updated by"
msgstr "Laatst gewijzigd door"
#. module: contract_publication
#: model:ir.model.fields,field_description:publication.field_distribution_list_write_date
msgid "Last Updated on"
msgstr "Laatst bijgewerkt op"
#. module: contract_publication
#: selection:product.template,publishing_frequency_type:0
msgid "Month(s)"
msgstr "Maanden"
#. module: contract_publication
#: model:ir.model.fields,field_description:publication.field_distribution_list_name
msgid "Name"
msgstr "Naam"
#. module: contract_publication
#: model:ir.model.fields,field_description:publication.field_distribution_list_assigned_count
msgid "Quantity already assigned"
msgstr "Aantal al toegekend"
#. module: contract_publication
#: model:ir.model.fields,field_description:publication.field_distribution_list_copies
msgid "Number of copies"
msgstr "Aantal exemplaren"
#. module: contract_publication
#: model:ir.model.fields,field_description:publication.field_distribution_list_available_count
msgid "Quantity still available"
msgstr "Aantal nog beschikbaar"
#. module: contract_publication
#: model:ir.model.fields,field_description:publication.field_distribution_list_contract_count
msgid "Quantity to distribute"
msgstr "Aantal exemplaren"
#. module: contract_publication
#: model:ir.model,name:publication.model_res_partner
#: model:ir.model.fields,field_description:publication.field_account_analytic_contract_line_partner_id
#: model:ir.model.fields,field_description:publication.field_account_analytic_invoice_line_partner_id
msgid "Partner"
msgstr "Relatie"
#. module: contract_publication
#: selection:product.template,distribution_type:0
msgid "Print"
msgstr "Drukwerk"
#. module: contract_publication
#: model:ir.model,name:publication.model_product_template
msgid "Product Template"
msgstr "Product"
#. module: contract_publication
#: model:ir.model.fields,field_description:publication.field_product_product_publication
#: model:ir.model.fields,field_description:publication.field_product_template_publication
msgid "Product is a publication?"
msgstr "Product is een publicatie"
#. module: contract_publication
#: model:ir.model.fields,field_description:publication.field_distribution_list_product_id
#: model:ir.ui.view,arch_db:publication.product_template_only_form_view
#: model:ir.ui.view,arch_db:publication.subscription_search
msgid "Publication"
msgstr "Publicatie"
#. module: contract_publication
#: model:ir.actions.act_window,name:publication.action_publication
#: model:ir.ui.menu,name:publication.menu_publication
msgid "Publications"
msgstr "Publicaties"
#. module: contract_publication
#: model:ir.model.fields,field_description:publication.field_res_partner_distribution_list_ids
#: model:ir.model.fields,field_description:publication.field_res_users_distribution_list_ids
msgid "Publications received"
msgstr "Publicaties"
#. module: contract_publication
#: model:ir.ui.view,arch_db:publication.view_partner_form
msgid "Publications to receive"
msgstr "Publicaties te ontvangen"
#. module: contract_publication
#: model:ir.model.fields,field_description:publication.field_product_product_publishing_frequency_interval
#: model:ir.model.fields,field_description:publication.field_product_template_publishing_frequency_interval
msgid "Published Every"
msgstr "Verschijnt iedere"
#. module: contract_publication
#: model:ir.model.fields,help:publication.field_product_product_publishing_frequency_interval
#: model:ir.model.fields,help:publication.field_product_template_publishing_frequency_interval
msgid "Published every (Days/Week/Month/Quarter/Year)"
msgstr "Herhaal elke (dagen/weken/maanden/jaren)"
#. module: contract_publication
#: model:ir.model.fields,field_description:publication.field_product_product_publishing_frequency_type
#: model:ir.model.fields,field_description:publication.field_product_template_publishing_frequency_type
msgid "Publishing frequency"
msgstr "Frequentie van verschijnen"
#. module: contract_publication
#: selection:product.template,publishing_frequency_type:0
msgid "Quarterly"
msgstr "Kwartaal"
#. module: contract_publication
#: model:ir.model.fields,field_description:publication.field_distribution_list_partner_id
#: model:ir.ui.view,arch_db:publication.subscription_search
msgid "Receiving Partner"
msgstr "Ontvangende relatie"
#. module: contract_publication
#: model:ir.model.fields,field_description:publication.field_distribution_list_display_address
msgid "Receiving address"
msgstr "Verzendadres"
#. module: contract_publication
#: model:ir.model.fields,help:publication.field_product_product_distribution_type
#: model:ir.model.fields,help:publication.field_product_template_distribution_type
msgid "Required if product is publication"
msgstr "Verplicht indien product een publicatie is"
#. module: contract_publication
#: model:ir.ui.view,arch_db:publication.subscription_form
msgid "Subscription Address"
msgstr "Verzendadres"
#. module: contract_publication
#: model:ir.model.fields,field_description:publication.field_res_partner_subscription_ids
#: model:ir.model.fields,field_description:publication.field_res_users_subscription_ids
#: model:ir.ui.view,arch_db:publication.view_partner_form
msgid "Subscription contract lines"
msgstr "Abonnementen"
#. module: contract_publication
#: model:ir.model.fields,field_description:publication.field_account_analytic_contract_line_publication
#: model:ir.model.fields,field_description:publication.field_account_analytic_invoice_line_publication
msgid "Subscription product line"
msgstr "Contractregel voor abonnement"
#. module: contract_publication
#: model:ir.ui.view,arch_db:publication.view_partner_form
#: model:ir.ui.view,arch_db:publication.subscription_search
#: model:ir.ui.view,arch_db:publication.subscription_tree
msgid "Subscriptions"
msgstr "Abonnementen"
#. module: contract_publication
#: selection:product.template,publishing_frequency_type:0
msgid "Week(s)"
msgstr "Week(en)"
#. module: contract_publication
#: selection:product.template,publishing_frequency_type:0
msgid "Year(s)"
msgstr "Jaar(en)"
#. module: contract_publication
#: model:ir.model,name:publication.model_account_analytic_invoice_line
msgid "account.analytic.invoice.line"
msgstr ""
#. module: contract_publication
#: model:ir.model,name:publication.model_distribution_list
msgid "distribution.list"
msgstr ""

6
contract_publication/models/__init__.py

@ -0,0 +1,6 @@
# -*- coding: utf-8 -*-
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from . import product_template
from . import account_analytic_invoice_line
from . import publication_distribution_list
from . import res_partner

33
contract_publication/models/account_analytic_invoice_line.py

@ -0,0 +1,33 @@
# -*- coding: utf-8 -*-
# Copyright 2014-2019 Therp BV <https://therp.nl>.
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
# pylint: disable=no-member,too-few-public-methods
from odoo import api, fields, models
class AccountAnalyticInvoiceLine(models.Model):
_inherit = 'account.analytic.invoice.line'
partner_id = fields.Many2one(
related='analytic_account_id.partner_id',
store=True,
readonly=True)
publication = fields.Boolean(
string='Subscription product line',
related='product_id.publication',
store=True)
@api.multi
def action_distribution_list(self):
self.ensure_one()
action = self.env.ref(
'publication.action_distribution_list').read()[0]
action['context'] = {
'default_product_id': self.product_id.id,
'default_contract_partner_id': self.partner_id.id}
action['domain'] = [
('contract_partner_id', '=', self.partner_id.id),
('product_id', '=', self.product_id.id)]
action['view_mode'] = 'form'
action['target'] = 'current'
return action

42
contract_publication/models/product_template.py

@ -0,0 +1,42 @@
# -*- coding: utf-8 -*-
# Copyright 2014-2019 Therp BV <https://therp.nl>.
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from odoo import api, fields, models
class ProductTemplate(models.Model):
_inherit = 'product.template'
publication = fields.Boolean(string='Product is a publication?')
distribution_type = fields.Selection(
selection=[('email', 'Electronic'), ('print', 'Print')],
string='Distribution',
help="Required if product is publication")
publishing_frequency_type = fields.Selection(
selection=[
('irregular', 'Irregular'),
('daily', 'Day(s)'),
('weekly', 'Week(s)'),
('monthly', 'Month(s)'),
('quarterly', 'Quarterly'),
('yearly', 'Year(s)')],
default='monthly',
string='Publishing frequency',
help="At what intervals publication is published")
publishing_frequency_interval = fields.Integer(
string='Published Every',
default=1,
help="Published every (Days/Week/Month/Quarter/Year)")
@api.multi
def action_distribution_list(self):
self.ensure_one()
action = self.env.ref(
'publication.action_distribution_list').read()[0]
action['context'] = {
'default_product_id': self.product_variant_ids[0].id}
action['domain'] = [
('product_id', '=', self.product_variant_ids[0].id)]
action['view_mode'] = 'form'
action['target'] = 'current'
return action

180
contract_publication/models/publication_distribution_list.py

@ -0,0 +1,180 @@
# -*- coding: utf-8 -*-
# Copyright 2014-2019 Therp BV <https://therp.nl>.
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import _, api, fields, models
from odoo.exceptions import ValidationError
SQL_CONTRACT_COUNT = """
SELECT COALESCE(SUM(ROUND(l.quantity)::integer), 0) as quantity
FROM account_analytic_invoice_line l
JOIN account_analytic_account c
ON l.analytic_account_id = c.id
WHERE l.product_id = %s
AND c.partner_id = %s"""
SQL_ASSIGNED_COUNT = """
SELECT COALESCE(SUM(copies), 0) as copies
FROM publication_distribution_list
WHERE product_id = %s
AND contract_partner_id = %s"""
class PublicationDistributionList(models.Model):
_name = 'publication.distribution.list'
_order = 'name'
@api.model
def get_product_contract_count(self, product_id, contract_partner_id):
self.env.cr.execute(
SQL_CONTRACT_COUNT,
(product_id, contract_partner_id))
return self.env.cr.fetchone()[0]
@api.model
def get_product_contract_assigned_count(
self, product_id, contract_partner_id):
self.env.cr.execute(
SQL_ASSIGNED_COUNT,
(product_id, contract_partner_id))
return self.env.cr.fetchone()[0]
@api.depends('product_id', 'partner_id')
def _compute_name_address(self):
"""Create subscription name from publication and partner."""
partner_model = self.env['res.partner']
for this in self:
if not this.product_id or not this.partner_id:
this.name = False
this.contact_address = False
continue
this.name = ' - '.join(
[this.product_id.name, this.partner_id.name])
if this.product_id.distribution_type == 'email':
this.contact_address = this.partner_id.email
else:
delivery_id = this.partner_id.address_get(
['delivery'])['delivery']
this.contact_address = partner_model.browse(
delivery_id).contact_address
@api.multi
@api.depends('product_id', 'contract_partner_id', 'copies')
def _compute_counts(self):
"""Used to check how many addresses can still be added."""
for this in self:
if not self.product_id or not self.contract_partner_id:
continue
contract_count = self.get_product_contract_count(
this.product_id.id, this.contract_partner_id.id)
assigned_count = self.get_product_contract_assigned_count(
this.product_id.id, this.contract_partner_id.id)
available_count = contract_count - assigned_count
this.contract_count = contract_count
this.assigned_count = assigned_count
this.available_count = available_count
product_id = fields.Many2one(
comodel_name='product.product',
string='Publication',
domain=[('publication', '=', True)],
required=True)
distribution_type = fields.Selection(
string='Type of publication',
related='product_id.distribution_type',
store=True)
partner_id = fields.Many2one(
comodel_name='res.partner',
string='Receiving Partner',
required=True)
contract_partner_id = fields.Many2one(
comodel_name='res.partner',
string='Contract Partner',
required=True)
date_start = fields.Date(
string='Date start',
default=fields.Date.today(),
required=True)
date_end = fields.Date(
string='Date end')
copies = fields.Integer(string='Number of copies', default=1)
name = fields.Char(
compute='_compute_name_address',
string='Name')
contact_address = fields.Char(
compute='_compute_name_address',
string='Receiving address')
contract_count = fields.Integer(
string="Quantity to distribute",
compute='_compute_counts')
assigned_count = fields.Integer(
string="Quantity already assigned",
compute='_compute_counts')
available_count = fields.Integer(
string="Quantity still available",
compute='_compute_counts')
@api.onchange('product_id', 'contract_partner_id')
def _onchange_keyfields(self):
"""Sets the proper domain for contract_partner.
Also enforces first selecting the publication.
"""
self.ensure_one()
if self.contract_partner_id and not self.partner_id:
self.partner_id = self.contract_partner_id
if self.contract_partner_id and not self.product_id:
raise ValidationError(_(
"You must select a publication before selecting"
" the contract partner."))
if not self.product_id:
return
valid_partners = []
line_model = self.env['account.analytic.invoice.line']
lines = line_model.search([('product_id', '=', self.product_id.id)])
for line in lines:
if line.analytic_account_id.partner_id.id not in valid_partners:
valid_partners.append(line.analytic_account_id.partner_id.id)
if not valid_partners:
raise ValidationError(_(
"There are no active subscriptions for this publication."))
partner_domain = [('id', 'in', valid_partners)]
return {'domain': {'contract_partner_id': partner_domain}}
@api.model
def create(self, vals):
result = super(PublicationDistributionList, self).create(vals)
result._limit_count()
return result
@api.multi
def write(self, vals):
result = super(PublicationDistributionList, self).write(vals)
self._limit_count()
return result
@api.multi
def _limit_count(self):
"""Limit number of copies send to amount set in contract lines.
It should be possible to make a constrains method of this function,
but for inexplicable reasons this does not work.
"""
for this in self:
# Do not use counts from 'this' as they probably have not
# been updated yet.
product = this.product_id
contract_partner = this.contract_partner_id
contract_count = self.get_product_contract_count(
product.id, contract_partner.id)
assigned_count = self.get_product_contract_assigned_count(
product.id, contract_partner.id)
available_count = contract_count - assigned_count
if available_count < 0:
raise ValidationError(_(
"Number of copies sent %d can not exceed contracted"
" number %d for partner %s and product %s" % (
assigned_count,
contract_count,
contract_partner.display_name,
product.display_name)))

18
contract_publication/models/res_partner.py

@ -0,0 +1,18 @@
# -*- coding: utf-8 -*-
# Copyright 2014-2019 Therp BV <https://therp.nl>.
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from odoo import fields, models
class ResPartner(models.Model):
_inherit = 'res.partner'
subscription_ids = fields.One2many(
comodel_name='account.analytic.invoice.line',
inverse_name='partner_id',
domain=[('publication', '=', True)],
string='Subscription contract lines')
distribution_list_ids = fields.One2many(
comodel_name='publication.distribution.list',
inverse_name='partner_id',
string='Publications received')

3
contract_publication/security/ir.model.access.csv

@ -0,0 +1,3 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_distribution_list_manager,CRUD access for distribution.list,model_publication_distribution_list,base.group_partner_manager,1,1,1,1
access_distribution_list_reader,READ access for distribution.list,model_publication_distribution_list,base.group_user,1,0,0,0

3
contract_publication/tests/__init__.py

@ -0,0 +1,3 @@
# -*- coding: utf-8 -*-
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from . import test_publication

163
contract_publication/tests/test_publication.py

@ -0,0 +1,163 @@
# -*- coding: utf-8 -*-
# Copyright 2018-2019 Therp BV <https://therp.nl>.
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from odoo.tests import common
from odoo.exceptions import ValidationError
class TestPublication(common.SavepointCase):
post_install = True
at_install = False
@classmethod
def setUpClass(cls):
super(TestPublication, cls).setUpClass()
# Create some test partners
partner_model = cls.env['res.partner']
cls.partner_jan = partner_jan = partner_model.create({
'name': 'Jan',
'city': 'Amsterdam'})
cls.partner_piet = partner_piet = partner_model.create({
'name': 'Piet',
'city': 'Rotterdam'})
cls.partner_joris = partner_model.create({
'name': 'Joris',
'city': 'Den Haag'})
cls.partner_corneel = partner_model.create({
'name': 'Corneel',
'city': 'Dordrecht'})
cls.partner_corneel_delivery = partner_model.create({
'name': 'Corneels store',
'parent_id': cls.partner_corneel.id,
'type': 'delivery',
'street': 'Wijnstraat 35',
'city': 'Dordrecht'})
# Make sure a sale journal is present for tests
sequence_model = cls.env['ir.sequence']
contract_sequence = sequence_model.create({
'company_id': cls.env.user.company_id.id,
'code': 'contract',
'name': 'contract sequence',
'number_next': 1,
'implementation': 'standard',
'padding': 3,
'number_increment': 1})
journal_model = cls.env['account.journal']
journal_model.create({
'company_id': cls.env.user.company_id.id,
'code': 'contract',
'name': 'contract journal',
'sequence_id': contract_sequence.id,
'type': 'sale'})
# Create products:
cls.uom_unit = cls.env.ref('product.product_uom_unit')
product_model = cls.env['product.product']
cls.product_book = product_book = product_model.create({
'name': 'Test yearbook',
'type': 'consu',
'uom_id': cls.uom_unit.id,
'uom_po_id': cls.uom_unit.id})
cls.product_newsletter = product_newsletter = product_model.create({
'name': 'Test newsletter',
'type': 'consu',
'publication': True,
'distribution_type': 'print',
'publishing_frequency_type': 'weekly',
'uom_id': cls.uom_unit.id,
'uom_po_id': cls.uom_unit.id})
# Create contract that will be subscription from the beginning:
contract_model = cls.env['account.analytic.account']
line_model = cls.env['account.analytic.invoice.line']
cls.contract_jan = contract_jan = contract_model.create({
'name': 'Test Contract Jan',
'partner_id': partner_jan.id,
'pricelist_id': partner_jan.property_product_pricelist.id,
'recurring_invoices': True,
'date_start': '2016-02-15',
'recurring_next_date': '2016-02-29'})
cls.line_newsletter = line_model.create({
'analytic_account_id': contract_jan.id,
'product_id': product_newsletter.id,
'name': 'Newsletter subscription',
'quantity': 40,
'uom_id': product_newsletter.uom_id.id,
'price_unit': 100,
'discount': 50})
# Create contract for yearbook. product will be changed to publication:
cls.contract_piet = contract_piet = contract_model.create({
'name': 'Test Contract Piet',
'partner_id': partner_piet.id,
'pricelist_id': partner_piet.property_product_pricelist.id,
'recurring_invoices': True,
'date_start': '2016-02-15',
'recurring_next_date': '2016-02-29'})
cls.line_book = line_model.create({
'analytic_account_id': contract_piet.id,
'product_id': product_book.id,
'name': 'Newsletter subscription',
'quantity': 1,
'uom_id': product_book.uom_id.id,
'price_unit': 100,
'discount': 50})
def test_contract(self):
"""Test creation of contract line for publication."""
line_newsletter = self.line_newsletter
partner_jan = self.partner_jan
self.assertTrue(line_newsletter.publication)
self.assertEqual(partner_jan.subscription_ids[0], line_newsletter)
def test_product_change(self):
"""Test change of product to publication product."""
product_book = self.product_book
line_book = self.line_book
partner_piet = self.partner_piet
self.assertFalse(line_book.publication)
self.assertFalse(bool(partner_piet.subscription_ids))
product_book.write({
'publication': True,
'distribution_type': 'print',
'publishing_frequency_type': 'yearly'})
self.assertTrue(line_book.publication)
self.assertEqual(partner_piet.subscription_ids[0], line_book)
def test_distribution_list(self):
"""Test adding recipients to distribution list."""
distribution_model = self.env['publication.distribution.list']
product_newsletter = self.product_newsletter
partner_jan = self.partner_jan
partner_joris = self.partner_joris
partner_corneel = self.partner_corneel
product_count = distribution_model.get_product_contract_count(
product_newsletter.id, partner_jan.id)
self.assertEqual(product_count, 40)
assigned_count = \
distribution_model.get_product_contract_assigned_count(
product_newsletter.id, partner_jan.id)
self.assertEqual(assigned_count, 0)
subscription01 = distribution_model.create({
'product_id': product_newsletter.id,
'contract_partner_id': partner_jan.id,
'partner_id': partner_joris.id,
'copies': 10})
self.assertEqual(subscription01.contract_count, 40)
self.assertEqual(subscription01.assigned_count, 10)
self.assertEqual(subscription01.available_count, 30)
subscription02 = distribution_model.create({
'product_id': product_newsletter.id,
'contract_partner_id': partner_jan.id,
'partner_id': partner_corneel.id,
'copies': 10})
self.assertEqual(subscription02.contract_count, 40)
# Old entry should also be changed
self.assertEqual(subscription01.assigned_count, 20)
self.assertEqual(subscription01.available_count, 20)
with self.assertRaises(ValidationError):
distribution_model.create({
'product_id': product_newsletter.id,
'contract_partner_id': partner_jan.id,
'partner_id': partner_jan.id,
'copies': 30})
# Delivery for Corneel should be to his store.
self.assertIn('Wijnstraat', subscription02.contact_address)

98
contract_publication/views/distribution_list.xml

@ -0,0 +1,98 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="subscription_search" model="ir.ui.view">
<field name="name">Search view for subscriptions</field>
<field name="model">publication.distribution.list</field>
<field name="arch" type="xml">
<search string="Subscriptions">
<field name="product_id" />
<field name="partner_id" />
<field name="contract_partner_id" />
<group expand="0" string="Group By...">
<filter
string="Publication"
context="{'group_by':'product_id'}"
/>
<filter
string="Receiving Partner"
context="{'group_by':'partner_id'}"
/>
<filter
string="Contract Partner"
context="{'group_by':'contract_partner_id'}"
/>
</group>
</search>
</field>
</record>
<record
id="subscription_tree"
model="ir.ui.view">
<field name="name">subscription.tree</field>
<field name="model">publication.distribution.list</field>
<field name="type">tree</field>
<field
name="arch"
type="xml"
>
<tree string="Subscriptions">
<field name="product_id" />
<field name="partner_id" />
<field name="contract_partner_id" />
<field name="contact_address" />
<field name="copies" />
</tree>
</field>
</record>
<record
id="subscription_form"
model="ir.ui.view"
>
<field name="name">subscription.form</field>
<field name="model">publication.distribution.list</field>
<field name="type">form</field>
<field
name="arch"
type="xml"
>
<form string="Subscription Address">
<group colspan="6" col="4">
<group colspan="4" col="4">
<field
name="product_id"
options="{'no_create': true, 'no_create_edit': true}"
/>
<field
name="contract_partner_id"
options="{'no_create': true, 'no_create_edit': true}"
/>
</group>
<group
attrs="{'invisible': ['|',('product_id','=',False),('contract_partner_id','=',False)]}"
colspan="4" col="6"
>
<field name="contract_count" />
<field name="assigned_count" />
<field name="available_count" />
<field
name="partner_id"
options="{'no_create': true, 'no_create_edit': true}"
/>
<field name="date_start" />
<field name="date_end" />
<field name="distribution_type" readonly="1" />
<field
name="copies"
attrs="{'invisible': [('distribution_type','=','email')]}"
/>
<field name="contact_address" />
</group>
</group>
</form>
</field>
</record>
</odoo>

13
contract_publication/views/menu.xml

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record
id="action_distribution_list"
model="ir.actions.act_window"
>
<field name="view_mode">tree,form</field>
<field name="res_model">publication.distribution.list</field>
<field name="name">Distribution List</field>
</record>
</odoo>

75
contract_publication/views/product_template.xml

@ -0,0 +1,75 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record
id="product_template_only_form_view"
model="ir.ui.view">
<field name="name">product.template.product.form.publication</field>
<field name="model">product.template</field>
<field
name="inherit_id"
ref="product.product_template_only_form_view"
/>
<field name="type">form</field>
<field
name="arch"
type="xml">
<div name="button_box" position="inside">
<button
name="action_distribution_list"
type="object"
class="oe_stat_button"
attrs="{'invisible': [('publication','=',False)]}"
icon="fa-truck"
>
<span>Distribution</span>
</button>
</div>
<xpath
expr="//div[@name='options']"
position="inside"
>
<field name="publication" />
<label for="publication" />
</xpath>
<xpath
expr="//page[last()]"
position="after"
>
<page
string="Publication"
attrs="{'invisible': [('publication','=',False)]}"
>
<group colspan="6" col="4">
<field name="distribution_type" />
<field name="publishing_frequency_type" />
<field name="publishing_frequency_interval" />
</group>
</page>
</xpath>
</field>
</record>
<record id="product_template_search_view" model="ir.ui.view">
<field name="name">product.template.search</field>
<field name="model">product.template</field>
<field
name="inherit_id"
ref="product.product_template_search_view"
/>
<field name="arch" type="xml">
<xpath
expr="//filter[@name='consumable']"
position="after"
>
<filter
string="Publications"
name="publications"
domain="[('publication', '=', True)]"
help="Publications (print or digital)"
/>
</xpath>
</field>
</record>
</odoo>

73
contract_publication/views/res_partner.xml

@ -0,0 +1,73 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record
id="view_partner_form"
model="ir.ui.view">
<field name="name">res.partner.publication.form</field>
<field name="model">res.partner</field>
<field name="type">form</field>
<field
name="inherit_id"
ref="base.view_partner_form"
/>
<field
name="arch"
type="xml">
<xpath
expr="//page[last()]"
position="after"
>
<page
string="Subscriptions"
>
<group colspan="6" col="4">
<separator
string="Subscription contract lines"
colspan="4"
/>
<field
name="subscription_ids"
nolabel="1"
colspan="4"
readonly="1"
>
<tree>
<field name="product_id" />
<field name="quantity" />
<field name="price_subtotal" />
<button
name="action_distribution_list"
type="object"
class="oe_stat_button"
icon="fa-truck"
>
</button>
</tree>
</field>
<separator
string="Publications to receive"
colspan="4"
/>
/>
<field
name="distribution_list_ids"
nolabel="1"
colspan="4"
readonly="1"
>
<tree>
<field name="product_id" />
<field name="copies" />
<field name="date_start" />
<field name="date_end" />
<field name="contract_partner_id" />
</tree>
</field>
</group>
</page>
</xpath>
</field>
</record>
</odoo>
Loading…
Cancel
Save