Browse Source

Merge pull request #123 from beescoop/9.0-procurement-order-cleanup

[ADD] beesdoo_base: cron to cleanup stale procurement orders
pull/126/head
Robin Keunen 5 years ago
committed by GitHub
parent
commit
2a0f3977d2
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 45
      beesdoo_base/__openerp__.py
  2. 58
      beesdoo_base/controllers/report.py
  3. 21
      beesdoo_base/data/cron.xml
  4. 4
      beesdoo_base/models/__init__.py
  5. 26
      beesdoo_base/models/membercard.py
  6. 122
      beesdoo_base/models/partner.py
  7. 57
      beesdoo_base/models/procurement.py
  8. 1
      beesdoo_base/wizard/__init__.py
  9. 47
      beesdoo_base/wizard/member_card.py
  10. 14
      beesdoo_base/wizard/partner.py
  11. 28
      beesdoo_base/wizard/portal_wizard.py

45
beesdoo_base/__openerp__.py

@ -1,29 +1,30 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
{ {
'name': "Beescoop Base Module",
'summary': """
"name": "Beescoop Base Module",
"summary": """
Module that customize the base module and contains some python tools Module that customize the base module and contains some python tools
""", """,
'description': """
"description": """
""", """,
'author': "Beescoop - Cellule IT",
'website': "https://github.com/beescoop/Obeesdoo",
'category': 'Project Management',
'version': '9.0.1.0.1',
'depends': ['point_of_sale', 'purchase', 'report', 'portal', 'partner_firstname'],
'data': [
'security/groups.xml',
'security/ir.model.access.csv',
'views/partner.xml',
'wizard/views/member_card.xml',
'wizard/views/partner.xml',
'data/default_contact.xml',
'report/beescard.xml',
"author": "Beescoop - Cellule IT",
"website": "https://github.com/beescoop/Obeesdoo",
"category": "Project Management",
"version": "9.0.1.0.1",
"depends": [
"point_of_sale",
"purchase",
"report",
"portal",
"partner_firstname",
],
"data": [
"security/groups.xml",
"security/ir.model.access.csv",
"data/cron.xml",
"views/partner.xml",
"wizard/views/member_card.xml",
"wizard/views/partner.xml",
"data/default_contact.xml",
"report/beescard.xml",
], ],
} }

58
beesdoo_base/controllers/report.py

@ -2,7 +2,10 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details. # Part of Odoo. See LICENSE file for full copyright and licensing details.
from openerp.addons.web.http import Controller, route, request from openerp.addons.web.http import Controller, route, request
from openerp.addons.web.controllers.main import _serialize_exception, content_disposition
from openerp.addons.web.controllers.main import (
_serialize_exception,
content_disposition,
)
from openerp.tools import html_escape from openerp.tools import html_escape
import json import json
@ -17,8 +20,7 @@ from openerp.tools.safe_eval import safe_eval
class ReportCustom(ReportController): class ReportCustom(ReportController):
@route(['/report/download'], type='http', auth="user")
@route(["/report/download"], type="http", auth="user")
def report_download(self, data, token): def report_download(self, data, token):
"""This function is used by 'qwebactionmanager.js' in order to trigger the download of """This function is used by 'qwebactionmanager.js' in order to trigger the download of
a pdf/controller report. a pdf/controller report.
@ -30,44 +32,54 @@ class ReportCustom(ReportController):
requestcontent = json.loads(data) requestcontent = json.loads(data)
url, type = requestcontent[0], requestcontent[1] url, type = requestcontent[0], requestcontent[1]
try: try:
if type == 'qweb-pdf':
reportname = url.split('/report/pdf/')[1].split('?')[0]
if type == "qweb-pdf":
reportname = url.split("/report/pdf/")[1].split("?")[0]
docids = None docids = None
if '/' in reportname:
reportname, docids = reportname.split('/')
if "/" in reportname:
reportname, docids = reportname.split("/")
if docids: if docids:
# Generic report: # Generic report:
response = self.report_routes(reportname, docids=docids, converter='pdf')
response = self.report_routes(
reportname, docids=docids, converter="pdf"
)
else: else:
# Particular report: # Particular report:
data = url_decode(url.split('?')[1]).items() # decoding the args represented in JSON
response = self.report_routes(reportname, converter='pdf', **dict(data))
data = url_decode(
url.split("?")[1]
).items() # decoding the args represented in JSON
response = self.report_routes(
reportname, converter="pdf", **dict(data)
)
cr, uid = request.cr, request.uid cr, uid = request.cr, request.uid
report = request.registry['report']._get_report_from_name(cr, uid, reportname)
report = request.registry["report"]._get_report_from_name(
cr, uid, reportname
)
filename = "%s.%s" % (report.name, "pdf") filename = "%s.%s" % (report.name, "pdf")
if docids: if docids:
ids = [int(x) for x in docids.split(",")] ids = [int(x) for x in docids.split(",")]
obj = request.env[report.model].browse(ids) obj = request.env[report.model].browse(ids)
if report.attachment and not len(obj) > 1: if report.attachment and not len(obj) > 1:
filename = safe_eval(report.attachment, {'object': obj, 'time': time})
response.headers.add('Content-Disposition', content_disposition(filename))
response.set_cookie('fileToken', token)
filename = safe_eval(
report.attachment, {"object": obj, "time": time}
)
response.headers.add(
"Content-Disposition", content_disposition(filename)
)
response.set_cookie("fileToken", token)
return response return response
elif type =='controller':
elif type == "controller":
reqheaders = Headers(request.httprequest.headers) reqheaders = Headers(request.httprequest.headers)
response = Client(request.httprequest.app, BaseResponse).get(url, headers=reqheaders, follow_redirects=True)
response.set_cookie('fileToken', token)
response = Client(request.httprequest.app, BaseResponse).get(
url, headers=reqheaders, follow_redirects=True
)
response.set_cookie("fileToken", token)
return response return response
else: else:
return return
except Exception, e:
except Exception as e:
se = _serialize_exception(e) se = _serialize_exception(e)
error = {
'code': 200,
'message': "Odoo Server Error",
'data': se
}
error = {"code": 200, "message": "Odoo Server Error", "data": se}
return request.make_response(html_escape(json.dumps(error))) return request.make_response(html_escape(json.dumps(error)))

21
beesdoo_base/data/cron.xml

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2019 Coop IT Easy
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
-->
<odoo>
<data noupdate="0">
<record id="cron_cleanup_procurement_order" model="ir.cron">
<field name="name">Procurement: cleanup procurement orders blocking the procurement rules</field>
<field name="active" eval="True"/>
<field name="user_id" ref="base.user_root"/>
<field name="interval_number">1</field>
<field name="interval_type">days</field>
<field name="numbercall">-1</field>
<field name="doall" eval="True"/>
<field name="model">procurement.order</field>
<field name="function">cron_cleanup_procurement_order</field>
<field name="args">()</field>
</record>
</data>
</odoo>

4
beesdoo_base/models/__init__.py

@ -1 +1,3 @@
import partner, membercard
from . import partner
from . import membercard
from . import procurement

26
beesdoo_base/models/membercard.py

@ -2,23 +2,33 @@
from openerp import models, fields, api from openerp import models, fields, api
import uuid import uuid
class MemberCard(models.Model):
class MemberCard(models.Model):
def _get_current_user(self): def _get_current_user(self):
return self.env.uid return self.env.uid
def _compute_bar_code(self): def _compute_bar_code(self):
rule = self.env['barcode.rule'].search([('name', '=', 'Customer Barcodes')])[0]
rule = self.env["barcode.rule"].search(
[("name", "=", "Customer Barcodes")]
)[0]
size = 13 - len(rule.pattern) size = 13 - len(rule.pattern)
ean = rule.pattern + str(uuid.uuid4().fields[-1])[:size] ean = rule.pattern + str(uuid.uuid4().fields[-1])[:size]
return ean[0:12] + str(self.env['barcode.nomenclature'].ean_checksum(ean))
return ean[0:12] + str(
self.env["barcode.nomenclature"].ean_checksum(ean)
)
_name = 'member.card'
_order = 'create_date desc'
_name = "member.card"
_order = "create_date desc"
valid = fields.Boolean(default=True, string="Active") valid = fields.Boolean(default=True, string="Active")
barcode = fields.Char("Barcode", oldname='ean13', default=_compute_bar_code)
partner_id = fields.Many2one('res.partner') #, default=_get_current_client)
responsible_id = fields.Many2one('res.users', default=_get_current_user, string="Responsible")
barcode = fields.Char(
"Barcode", oldname="ean13", default=_compute_bar_code
)
partner_id = fields.Many2one(
"res.partner"
) # , default=_get_current_client)
responsible_id = fields.Many2one(
"res.users", default=_get_current_user, string="Responsible"
)
end_date = fields.Date(readonly=True, string="Expiration Date") end_date = fields.Date(readonly=True, string="Expiration Date")
comment = fields.Char("Reason", required=True) comment = fields.Char("Reason", required=True)

122
beesdoo_base/models/partner.py

@ -2,28 +2,55 @@
from openerp import models, fields, api, _ from openerp import models, fields, api, _
from openerp.exceptions import ValidationError from openerp.exceptions import ValidationError
class Partner(models.Model):
_inherit = 'res.partner'
class Partner(models.Model):
eater = fields.Selection([('eater', 'Eater'), ('worker_eater', 'Worker and Eater')], string="Eater/Worker")
child_eater_ids = fields.One2many("res.partner", "parent_eater_id", domain=[('customer', '=', True),
('eater', '=', 'eater')])
parent_eater_id = fields.Many2one("res.partner", string="Parent Worker", readonly=True)
barcode = fields.Char(compute="_get_bar_code", string='Barcode', store=True)
parent_barcode = fields.Char(compute="_get_bar_code", string='Parent Barcode', store=True)
member_card_ids = fields.One2many('member.card', 'partner_id')
country_id = fields.Many2one(required=True, default=lambda self: self.env.ref('base.be'))
_inherit = "res.partner"
member_card_to_be_printed = fields.Boolean('Print BEES card?')
last_printed = fields.Datetime('Last printed on')
cooperator_type = fields.Selection([('share_a', 'Share A'), ('share_b', 'Share B'), ('share_c', 'Share C')], store=True, compute=None)
eater = fields.Selection(
[("eater", "Eater"), ("worker_eater", "Worker and Eater")],
string="Eater/Worker",
)
child_eater_ids = fields.One2many(
"res.partner",
"parent_eater_id",
domain=[("customer", "=", True), ("eater", "=", "eater")],
)
parent_eater_id = fields.Many2one(
"res.partner", string="Parent Worker", readonly=True
)
barcode = fields.Char(
compute="_get_bar_code", string="Barcode", store=True
)
parent_barcode = fields.Char(
compute="_get_bar_code", string="Parent Barcode", store=True
)
member_card_ids = fields.One2many("member.card", "partner_id")
country_id = fields.Many2one(
required=True, default=lambda self: self.env.ref("base.be")
)
member_card_to_be_printed = fields.Boolean("Print BEES card?")
last_printed = fields.Datetime("Last printed on")
cooperator_type = fields.Selection(
[
("share_a", "Share A"),
("share_b", "Share B"),
("share_c", "Share C"),
],
store=True,
compute=None,
)
@api.one @api.one
@api.depends('parent_eater_id', 'parent_eater_id.barcode', 'eater', 'member_card_ids')
@api.depends(
"parent_eater_id",
"parent_eater_id.barcode",
"eater",
"member_card_ids",
)
def _get_bar_code(self): def _get_bar_code(self):
if self.eater == 'eater':
if self.eater == "eater":
self.parent_barcode = self.parent_eater_id.barcode self.parent_barcode = self.parent_eater_id.barcode
elif self.member_card_ids: elif self.member_card_ids:
for c in self.member_card_ids: for c in self.member_card_ids:
@ -31,7 +58,7 @@ class Partner(models.Model):
self.barcode = c.barcode self.barcode = c.barcode
@api.one @api.one
@api.constrains('child_eater_ids', 'parent_eater_id')
@api.constrains("child_eater_ids", "parent_eater_id")
def _check_number_of_eaters(self): def _check_number_of_eaters(self):
"""The owner of an A share can have a maximum of two eaters but """The owner of an A share can have a maximum of two eaters but
the owner of a B share can have a maximum of three eaters. the owner of a B share can have a maximum of three eaters.
@ -40,52 +67,65 @@ class Partner(models.Model):
share_type_code = self.cooperator_type share_type_code = self.cooperator_type
parent_share_type_code = self.parent_eater_id.cooperator_type parent_share_type_code = self.parent_eater_id.cooperator_type
# Raise exception # Raise exception
if share_type_code == 'share_b' or parent_share_type_code == 'share_b':
if len(self.child_eater_ids) > 3 or len(self.parent_eater_id.child_eater_ids) > 3:
raise ValidationError(_('You can only set three additional eaters per worker'))
if share_type_code == "share_b" or parent_share_type_code == "share_b":
if (
len(self.child_eater_ids) > 3
or len(self.parent_eater_id.child_eater_ids) > 3
):
raise ValidationError(
_("You can only set three additional eaters per worker")
)
else: else:
if len(self.child_eater_ids) > 2 or len(self.parent_eater_id.child_eater_ids) > 2:
raise ValidationError(_('You can only set two additional eaters per worker'))
if (
len(self.child_eater_ids) > 2
or len(self.parent_eater_id.child_eater_ids) > 2
):
raise ValidationError(
_("You can only set two additional eaters per worker")
)
@api.multi @api.multi
def write(self, values): def write(self, values):
if values.get('parent_eater_id') and self.parent_eater_id:
raise ValidationError(_('You try to assign a eater to a worker but this easer is alread assign to %s please remove it before') % self.parent_eater_id.name)
if values.get("parent_eater_id") and self.parent_eater_id:
raise ValidationError(
_(
"You try to assign a eater to a worker but this easer is alread assign to %s please remove it before"
)
% self.parent_eater_id.name
)
# replace many2many command when writing on child_eater_ids to just remove the link # replace many2many command when writing on child_eater_ids to just remove the link
if 'child_eater_ids' in values:
for command in values['child_eater_ids']:
if "child_eater_ids" in values:
for command in values["child_eater_ids"]:
if command[0] == 2: if command[0] == 2:
command[0] = 3 command[0] = 3
return super(Partner, self).write(values) return super(Partner, self).write(values)
@api.one @api.one
def _deactivate_active_cards(self): def _deactivate_active_cards(self):
for card in self.member_card_ids.filtered('valid'):
for card in self.member_card_ids.filtered("valid"):
card.valid = False card.valid = False
card.end_date = fields.Date.today() card.end_date = fields.Date.today()
@api.multi @api.multi
def _new_card(self, reason, user_id, barcode=False): def _new_card(self, reason, user_id, barcode=False):
card_data = { card_data = {
'partner_id' : self.id,
'responsible_id' : user_id,
'comment' : reason,
"partner_id": self.id,
"responsible_id": user_id,
"comment": reason,
} }
if barcode: if barcode:
card_data['barcode'] = barcode
self.env['member.card'].create(card_data)
card_data["barcode"] = barcode
self.env["member.card"].create(card_data)
@api.multi @api.multi
def _new_eater(self, surname, name, email): def _new_eater(self, surname, name, email):
partner_data = { partner_data = {
'lastname' : name,
'firstname' : surname,
'is_customer' : True,
'eater' : 'eater',
'parent_eater_id' : self.id,
'email' : email,
'country_id' : self.country_id.id
"lastname": name,
"firstname": surname,
"is_customer": True,
"eater": "eater",
"parent_eater_id": self.id,
"email": email,
"country_id": self.country_id.id,
} }
return self.env['res.partner'].create(partner_data)
return self.env["res.partner"].create(partner_data)

57
beesdoo_base/models/procurement.py

@ -0,0 +1,57 @@
# Copyright 2019 Coop IT Easy SCRL fs
# Robin Keunen <robin@coopiteasy.be>
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
from openerp import models, fields, api
import logging
_logger = logging.getLogger(__name__)
class ProcurementOrder(models.Model):
_inherit = "procurement.order"
@api.model
def cron_cleanup_procurement_order(self):
running_procurement = self.env["procurement.order"].search(
[("state", "=", "running")]
)
# checks if procurement order is done
# -> if all moves are 'done' or 'cancel'
_logger.info("check procurements in running state.")
running_procurement.check()
# in remaining procurement, cancel those with no procurement order
unlinked_procurement = self.env["procurement.order"].search(
[("state", "=", "running"), ("purchase_line_id", "=", False)]
)
_logger.info(
"cancelling %s procurement unlinked from PO."
% len(unlinked_procurement)
)
unlinked_procurement.cancel()
# in remaining procurement, delete those from 'done' purchase order
procurement_linked_to_done_PO = self.env["procurement.order"].search(
[
("state", "=", "running"),
("purchase_line_id", "!=", False),
("purchase_line_id.order_id.state", "=", "done"),
]
)
_logger.info(
"set %s procurement to done (linked to done PO)"
% len(procurement_linked_to_done_PO)
)
procurement_linked_to_done_PO.write({"state": "done"})
# cancel procurement order from exceptions
exception_procurement = self.env["procurement.order"].search(
[("state", "=", "exception"), ("purchase_line_id", "=", False)]
)
_logger.info(
"cancelling %s procurement in exception"
% len(exception_procurement)
)
exception_procurement.cancel()

1
beesdoo_base/wizard/__init__.py

@ -1,2 +1 @@
import member_card, partner, portal_wizard import member_card, partner, portal_wizard

47
beesdoo_base/wizard/member_card.py

@ -1,52 +1,67 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from openerp import models, fields, api from openerp import models, fields, api
class NewMemberCardWizard(models.TransientModel): class NewMemberCardWizard(models.TransientModel):
""" """
A transient model for the creation of a new card. A transient model for the creation of a new card.
The user can only define the raison why a new card is The user can only define the raison why a new card is
needed and the eater/worker that is concerned. needed and the eater/worker that is concerned.
""" """
_name = 'membercard.new.wizard'
_name = "membercard.new.wizard"
def _get_default_partner(self): def _get_default_partner(self):
return self.env.context['active_id']
return self.env.context["active_id"]
new_comment = fields.Char('Reason', required=True)
partner_id = fields.Many2one('res.partner', default=_get_default_partner)
force_barcode = fields.Char('Force Barcode', groups="beesdoo_base.group_force_barcode")
new_comment = fields.Char("Reason", required=True)
partner_id = fields.Many2one("res.partner", default=_get_default_partner)
force_barcode = fields.Char(
"Force Barcode", groups="beesdoo_base.group_force_barcode"
)
@api.one @api.one
def create_new_card(self): def create_new_card(self):
client = self.partner_id.sudo() client = self.partner_id.sudo()
client._deactivate_active_cards() client._deactivate_active_cards()
client._new_card(self.new_comment, self.env.uid, barcode=self.force_barcode)
client._new_card(
self.new_comment, self.env.uid, barcode=self.force_barcode
)
client.member_card_to_be_printed = True client.member_card_to_be_printed = True
class RequestMemberCardPrintingWizard(models.TransientModel): class RequestMemberCardPrintingWizard(models.TransientModel):
_name = 'membercard.requestprinting.wizard'
_name = "membercard.requestprinting.wizard"
def _get_selected_partners(self): def _get_selected_partners(self):
return self.env.context['active_ids']
partner_ids = fields.Many2many('res.partner', default=_get_selected_partners)
return self.env.context["active_ids"]
partner_ids = fields.Many2many(
"res.partner", default=_get_selected_partners
)
@api.one @api.one
def request_printing(self): def request_printing(self):
self.partner_ids.write({'member_card_to_be_printed' : True})
self.partner_ids.write({"member_card_to_be_printed": True})
class SetAsPrintedWizard(models.TransientModel): class SetAsPrintedWizard(models.TransientModel):
_name = 'membercard.set_as_printed.wizard'
_name = "membercard.set_as_printed.wizard"
def _get_selected_partners(self): def _get_selected_partners(self):
return self.env.context['active_ids']
return self.env.context["active_ids"]
partner_ids = fields.Many2many('res.partner', default=_get_selected_partners)
partner_ids = fields.Many2many(
"res.partner", default=_get_selected_partners
)
@api.one @api.one
def set_as_printed(self): def set_as_printed(self):
self.partner_ids.write({'member_card_to_be_printed' : False,
'last_printed' : fields.Datetime.now()})
self.partner_ids.write(
{
"member_card_to_be_printed": False,
"last_printed": fields.Datetime.now(),
}
)

14
beesdoo_base/wizard/partner.py

@ -1,20 +1,22 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from openerp import models, fields, api from openerp import models, fields, api
class NewEaterWizard(models.TransientModel): class NewEaterWizard(models.TransientModel):
""" """
A transient model for the creation of a eater related to a worker. A transient model for the creation of a eater related to a worker.
""" """
_name = 'eater.new.wizard'
_name = "eater.new.wizard"
def _get_default_partner(self): def _get_default_partner(self):
return self.env.context['active_id']
return self.env.context["active_id"]
first_name = fields.Char('First Name', required=True)
last_name = fields.Char('Last Name', required=True)
email = fields.Char('Email')
first_name = fields.Char("First Name", required=True)
last_name = fields.Char("Last Name", required=True)
email = fields.Char("Email")
partner_id = fields.Many2one('res.partner', default=_get_default_partner)
partner_id = fields.Many2one("res.partner", default=_get_default_partner)
@api.one @api.one
def create_new_eater(self): def create_new_eater(self):

28
beesdoo_base/wizard/portal_wizard.py

@ -1,26 +1,30 @@
from openerp import models, fields, api from openerp import models, fields, api
from openerp import SUPERUSER_ID from openerp import SUPERUSER_ID
class BeesdooWizard(models.TransientModel): class BeesdooWizard(models.TransientModel):
_inherit = 'portal.wizard'
_inherit = "portal.wizard"
@api.onchange('portal_id')
@api.onchange("portal_id")
def onchange_portal(self): def onchange_portal(self):
# for each partner, determine corresponding portal.wizard.user records # for each partner, determine corresponding portal.wizard.user records
res_partner = self.env['res.partner']
partner_ids = self._context.get('active_ids', [])
res_partner = self.env["res.partner"]
partner_ids = self._context.get("active_ids", [])
contact_ids = set() contact_ids = set()
for partner in res_partner.browse(partner_ids): for partner in res_partner.browse(partner_ids):
for contact in (partner.child_ids | partner):
for contact in partner.child_ids | partner:
# make sure that each contact appears at most once in the list # make sure that each contact appears at most once in the list
if contact.id not in contact_ids: if contact.id not in contact_ids:
contact_ids.add(contact.id) contact_ids.add(contact.id)
in_portal = self.portal_id in contact.user_ids.mapped('groups_id')
self.user_ids |= self.env['portal.wizard.user'].new({
'partner_id': contact.id,
'email': contact.email,
'in_portal': in_portal,
})
in_portal = self.portal_id in contact.user_ids.mapped(
"groups_id"
)
self.user_ids |= self.env["portal.wizard.user"].new(
{
"partner_id": contact.id,
"email": contact.email,
"in_portal": in_portal,
}
)
Loading…
Cancel
Save