Browse Source

Merge commit '428b5a87e862d422af23bd418dc92bf7ac082b2d' into 9.0-S0016-jeanmarc

pull/14/head
EliseDup 8 years ago
parent
commit
1e193151f2
  1. 5
      README.md
  2. 1
      beesdoo_base/__init__.py
  3. 4
      beesdoo_base/__openerp__.py
  4. 47
      beesdoo_base/models/membercard.py
  5. 50
      beesdoo_base/models/partner.py
  6. 6
      beesdoo_base/security/groups.xml
  7. 134
      beesdoo_base/views/partner.xml
  8. 2
      beesdoo_base/wizard/__init__.py
  9. 52
      beesdoo_base/wizard/member_card.py
  10. 82
      beesdoo_base/wizard/views/member_card.xml
  11. 3
      beesdoo_coda/__init__.py
  12. 22
      beesdoo_coda/__openerp__.py
  13. 1
      beesdoo_coda/models/__init__.py
  14. 12
      beesdoo_coda/models/bank_statement.py
  15. 6
      beesdoo_coda/wizard/__init__.py
  16. 77
      beesdoo_coda/wizard/import_coda.py
  17. 22
      beesdoo_migration_asbl_to_coop/__init__.py
  18. 40
      beesdoo_migration_asbl_to_coop/__openerp__.py
  19. 220
      beesdoo_migration_asbl_to_coop/migration.py
  20. 31
      beesdoo_migration_asbl_to_coop/view/migration.xml
  21. 4
      beesdoo_pos/__openerp__.py
  22. 6
      beesdoo_pos/data/email.xml
  23. 27
      beesdoo_pos/models/beesdoo_pos.py
  24. 24
      beesdoo_pos/static/src/css/beesdoo.css
  25. 63
      beesdoo_pos/static/src/js/beesdoo.js
  26. 39
      beesdoo_pos/static/src/xml/templates.xml
  27. 24
      beesdoo_pos/views/beesdoo_pos.xml
  28. 3
      beesdoo_product/__init__.py
  29. 5
      beesdoo_product/__openerp__.py
  30. 4
      beesdoo_product/data/product_label.xml
  31. 45
      beesdoo_product/models/beesdoo_product.py
  32. 49
      beesdoo_product/views/beesdoo_product.xml
  33. 1
      beesdoo_product/wizard/__init__.py
  34. 21
      beesdoo_product/wizard/label_printing_utils.py
  35. 55
      beesdoo_product/wizard/views/label_printing_utils.xml
  36. 4
      beesdoo_project/__openerp__.py
  37. 11
      beesdoo_project/models/task.py
  38. 2
      beesdoo_purchase/__init__.py
  39. 31
      beesdoo_purchase/__openerp__.py
  40. 2
      beesdoo_purchase/models/__init__.py
  41. 1
      beesdoo_purchase/security/ir.model.access.csv
  42. 14
      beesdoo_purchase/views/purchase_order.xml
  43. 25
      import_base/__init__.py
  44. 42
      import_base/__openerp__.py
  45. 415
      import_base/import_framework.py
  46. 220
      import_base/mapper.py
  47. 23
      import_odoo/__init__.py
  48. 41
      import_odoo/__openerp__.py
  49. 38
      import_odoo/odoo_connector.py
  50. 67
      import_odoo/view/connector.xml
  51. 69
      web_environment_ribbon/README.rst
  52. 20
      web_environment_ribbon/__init__.py
  53. 40
      web_environment_ribbon/__openerp__.py
  54. 12
      web_environment_ribbon/data/ribbon_data.xml
  55. BIN
      web_environment_ribbon/static/description/icon.png
  56. BIN
      web_environment_ribbon/static/description/screenshot.png
  57. 24
      web_environment_ribbon/static/src/css/ribbon.css
  58. 35
      web_environment_ribbon/static/src/js/ribbon.js
  59. 21
      web_environment_ribbon/view/base_view.xml

5
README.md

@ -1,9 +1,10 @@
# Obeesdoo
Specific module for the Beescoop
# Migrate barcode
```sql
insert into member_card (active, barcode, partner_id, responsible_id, activation_date) select 't', barcode, id, 1, '2016-01-01' from res_partner where barcode is not null;
update res_partner set eater = 'worker_eater' where barcode is not null;
```
```

1
beesdoo_base/__init__.py

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
import models
import wizard
import tools

4
beesdoo_base/__openerp__.py

@ -15,10 +15,12 @@
'category': 'Project Management',
'version': '0.1',
'depends': ['point_of_sale'],
'depends': ['point_of_sale', 'purchase'],
'data': [
'security/groups.xml',
'security/ir.model.access.csv',
'views/partner.xml',
'wizard/views/member_card.xml',
],
}

47
beesdoo_base/models/membercard.py

@ -1,47 +1,24 @@
# -*- coding: utf-8 -*-
from openerp import models, fields, api
from random import randint
import uuid
class MemberCard(models.Model):
def _get_current_user(self):
return self.env.uid
def _get_current_client(self):
# TODO : this does not work
return self.env['res.partner'].search([('id', '=',self.env.context['active_id'])])
def _compute_bar_code(self):
rule = self.env['barcode.rule'].search([('name', '=', 'Customer Barcodes')])[0]
nomenclature = self.env['barcode.nomenclature']
size = 13-len(rule.pattern)
ean = rule.pattern + str(uuid.uuid4().fields[-1])[:size] #str(randint(10**(size-1), 10**size-1))
code = ean[0:12] + str(nomenclature.ean_checksum(ean))
nomenclature.check_encoding(code,'ean13')
return code
size = 13 - len(rule.pattern)
ean = rule.pattern + str(uuid.uuid4().fields[-1])[:size]
return ean[0:12] + str(self.env['barcode.nomenclature'].ean_checksum(ean))
_name = 'member.card'
_order = 'activation_date desc'
_order = 'create_date desc'
valid = fields.Boolean(default=True, string="Active")
barcode = fields.Char("Code barre", oldname='ean13', default=_compute_bar_code)
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="Responsable")
activation_date = fields.Date(default=fields.Date.today, readonly=True, string="Date de création")
end_date = fields.Date(readonly=True, string="Date d'expiration")
comment = fields.Char("Raison", required=True)
# A transient model for the creation of a new card. The user can only define the raison why
# a new card is needed and the eater/worker that is concerned.
class MemberCardWizard(models.TransientModel):
_name = 'membercard.wizard'
new_comment = fields.Char('Raison', required=True)
@api.multi
def create_new_card(self):
client = self.env['res.partner'].search([('id', '=',self.env.context['active_id'])])
client._deactivate_active_cards()
client._new_card(self.new_comment)
responsible_id = fields.Many2one('res.users', default=_get_current_user, string="Responsible")
end_date = fields.Date(readonly=True, string="Expiration Date")
comment = fields.Char("Reason", required=True)

50
beesdoo_base/models/partner.py

@ -8,20 +8,19 @@ class Partner(models.Model):
_inherit = 'res.partner'
first_name = fields.Char('First Name')
last_name = fields.Char('Last Name', required=True)
eater = fields.Selection([('eater', 'Mangeur'), ('worker_eater', 'Mangeur et Travailleur')], string="Mangeur/Travailleur")
last_name = fields.Char('Last Name')
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 Travailleur", readonly=True)
barcode = fields.Char(compute="_get_bar_code", string='Code Barre', store=True)
parent_barcode = fields.Char(compute="_get_bar_code", string='Code Barre du Parent', store=True)
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('res.country', string='Country', required=True)
member_card_to_be_printed = fields.Boolean('Print BEES card?')
last_printed = fields.Datetime('Last printed on')
@api.onchange('first_name', 'last_name')
def _on_change_name(self):
self.name = concat_names(self.first_name, self.last_name)
@ -52,15 +51,28 @@ class Partner(models.Model):
if command[0] == 2:
command[0] = 3
return super(Partner, self).write(values)
@api.multi
@api.one
def _deactivate_active_cards(self):
if len(self.member_card_ids) > 0:
for c in self.member_card_ids:
if c.valid:
c.valid = False
c.end_date = fields.Date.today()
@api.multi
def _new_card(self, txt):
self.env['member.card'].create({'partner_id' : self.env.context['active_id'],'comment' : txt})
for card in self.member_card_ids.filtered('valid'):
card.valid = False
card.end_date = fields.Date.today()
@api.multi
def _new_card(self, reason, user_id, barcode=False):
card_data = {
'partner_id' : self.id,
'responsible_id' : user_id,
'comment' : reason,
}
if barcode:
card_data['barcode'] = barcode
self.env['member.card'].create(card_data)
@api.noguess
def _auto_init(self, cr, context=None):
res = super(Partner, self)._auto_init(cr, context=context)
cr.execute("UPDATE res_partner set last_name = name where last_name IS NULL")
return res

6
beesdoo_base/security/groups.xml

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="group_force_barcode" model="res.groups">
<field name="name">Bees Card Force Barcode</field>
</record>
</odoo>

134
beesdoo_base/views/partner.xml

@ -1,74 +1,72 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record model="ir.actions.act_window" id="action_membercard_wizard">
<field name="name">New Member Card</field>
<field name="res_model">membercard.new.wizard</field>
<field name="view_mode">form</field>
<field name="target">new</field>
</record>
<record id="MemberCard Wizard" model="ir.ui.view">
<field name="name">New MemberCard Wizard</field>
<field name="model">membercard.wizard</field>
<field name="arch" type="xml">
<form>
<group>
<field name="new_comment" string="Raison" editable="True" />
</group>
<footer>
<button type="object" name="create_new_card" string="Créer"
class="oe_highlight" />
<button special="cancel" string="Annuler" />
</footer>
</form>
</field>
</record>
<act_window id="action_membercard_wizard"
name="Créer une nouvelle carte de membre - Désactiver l'ancienne"
src_model="member.card" res_model="membercard.wizard" view_mode="form"
target="new" multi="True" />
<record model="ir.ui.view" id="beesdoo_partner_form_view">
<field name="name">beesdoo.partner.form.view</field>
<field name="model">res.partner</field>
<field name="inherit_id" ref="point_of_sale.view_partner_property_form" />
<field name="arch" type="xml">
<field name="name" position="replace">
<field name="name" class="oe_read_only" />
<field name="first_name" placeholder="First Name" class="oe_edit_only"
attrs="{'invisible' : [('company_type', '=', 'company')]}" />
<field name="last_name" placeholder="Last Name" class="oe_edit_only"
default_focus="1" />
</field>
<field name="website" position="after">
<field name="eater"
attrs="{'invisible': [('company_type', '=', 'company')]}" />
<field name="parent_eater_id" attrs="{'invisible' : [('eater', '!=', 'eater')]}" />
</field>
<xpath expr="//notebook" position="inside">
<page string="Member Cards"
attrs="{'invisible': ['|', ('customer', '=', False), ('eater', '!=', 'worker_eater')]}">
<separator string="Printing" />
<group>
<field name="member_card_to_be_printed" />
<field name="last_printed" />
</group>
<separator string="Eaters" />
<field name="child_eater_ids" widget="many2many_tags" />
<separator string="Cards" />
<field string="Cards" name="member_card_ids">
<tree editable="bottom">
<field name="barcode" />
<field name="create_date" />
<field name="end_date" />
<field name="responsible_id" />
<field name="comment" />
<field name="valid" />
</tree>
</field>
<group>
<button string="New Card" name="%(action_membercard_wizard)d"
type="action" />
</group>
</page>
</xpath>
<field name="barcode" position="attributes">
<attribute name="attrs">{'invisible' : [('eater', '!=', 'worker_eater')]}</attribute>
</field>
<field name="barcode" position="after">
<field name="parent_barcode" attrs="{'invisible' : [('eater', '!=', 'eater')]}" />
</field>
</field>
</record>
<record model="ir.ui.view" id="beesdoo_partner_form_view">
<field name="name">beesdoo.partner.form.view</field>
<field name="model">res.partner</field>
<field name="inherit_id" ref="point_of_sale.view_partner_property_form" />
<field name="arch" type="xml">
<field name="name" position="replace">
<field name="name" class="oe_read_only" />
<field name="first_name" placeholder="First Name" class="oe_edit_only"
attrs="{'invisible' : [('company_type', '=', 'company')]}" />
<field name="last_name" placeholder="Last Name" class="oe_edit_only"
default_focus="1" />
</field>
<field name="website" position="after">
<field name="eater"
attrs="{'invisible': [('company_type', '=', 'company')]}" />
<field name="parent_eater_id" attrs="{'invisible' : [('eater', '!=', 'eater')]}" />
</field>
<xpath expr="//notebook" position="inside">
<page string="Carte de Membre"
attrs="{'invisible': ['|', ('customer', '=', False), ('eater', '!=', 'worker_eater')]}">
<separator string="Mangeurs" />
<field name="child_eater_ids" widget="many2many_tags" />
<separator string="Cartes" />
<field string="Cartes" name="member_card_ids">
<tree editable="bottom">
<field name="barcode" />
<field name="activation_date" />
<field name="end_date" />
<field name="responsible_id" />
<field name="comment" />
<field name="valid" />
</tree>
</field>
<group>
<button string="Nouvelle Carte" name="%(action_membercard_wizard)d"
type="action" />
</group>
</page>
</xpath>
<field name="barcode" position="attributes">
<attribute name="attrs">{'invisible' : [('eater', '!=',
'worker_eater')]}</attribute>
</field>
<field name="barcode" position="after">
<field name="parent_barcode" attrs="{'invisible' : [('eater', '!=', 'eater')]}" />
</field>
<!-- S022 : By default a supplier should be a company -->
<record id="base.action_partner_supplier_form" model="ir.actions.act_window">
<field name="context">{ 'search_default_supplier': 1,
'default_customer': 0,
'default_supplier': 1,
'default_is_company' : True,
'default_company_type' : 'company', }
</field>
</record>
</odoo>
</odoo>

2
beesdoo_base/wizard/__init__.py

@ -0,0 +1,2 @@
import member_card

52
beesdoo_base/wizard/member_card.py

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

82
beesdoo_base/wizard/views/member_card.xml

@ -0,0 +1,82 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- New card generation wizard -->
<record id="MemberCard Wizard" model="ir.ui.view">
<field name="name">New MemberCard Wizard</field>
<field name="model">membercard.new.wizard</field>
<field name="arch" type="xml">
<form>
<group groups="beesdoo_base.group_force_barcode">
<field name="force_barcode" />
</group>
<separator string="Reason" />
<field name="new_comment" string="Raison" editable="True" />
<field name="partner_id" invisible="1" />
<footer>
<button type="object" name="create_new_card" string="Create"
class="oe_highlight" />
<button special="cancel" string="Cancel" />
</footer>
</form>
</field>
</record>
<record id="printing_membercard_request_wizard" model="ir.ui.view">
<field name="name">Request Membercard Printing Wizard</field>
<field name="model">membercard.requestprinting.wizard</field>
<field name="arch" type="xml">
<form>
<separator string="Request Printing for" />
<field name="partner_ids" />
<footer>
<button
type="object"
name="request_printing"
string="Request Beescard Printing"
class="oe_highlight" />
<button special="cancel" string="Cancel" />
</footer>
</form>
</field>
</record>
<act_window name="Request BEES card printing"
res_model="membercard.requestprinting.wizard"
src_model="res.partner"
view_mode="form"
target="new"
key2="client_action_multi"
id="beesdoo_base_action_request_membercard_printing"
/>
<record id="membercard_set_as_printed_wizard" model="ir.ui.view">
<field name="name">Set Membercard as Printed Wizard</field>
<field name="model">membercard.set_as_printed.wizard</field>
<field name="arch" type="xml">
<form>
<separator string="Set as Printed for" />
<field name="partner_ids" />
<footer>
<button
type="object"
name="set_as_printed"
string="Set as Printed"
class="oe_highlight" />
<button special="cancel" string="Cancel" />
</footer>
</form>
</field>
</record>
<act_window name="Set BEES card as printed"
res_model="membercard.set_as_printed.wizard"
src_model="res.partner"
view_mode="form"
target="new"
key2="client_action_multi"
id="beesdoo_base_action_set_membercard_as_printed"
/>
</odoo>

3
beesdoo_coda/__init__.py

@ -0,0 +1,3 @@
# -*- coding: utf-8 -*-
import wizard
import models

22
beesdoo_coda/__openerp__.py

@ -0,0 +1,22 @@
# -*- coding: utf-8 -*-
{
'name': "Beescoop Coda Import module",
'summary': """
Import coda Wizard based on https://github.com/acsone/pycoda
""",
'description': """
""",
'author': "Beescoop - Cellule IT",
'website': "https://github.com/beescoop/Obeesdoo",
'category': 'Accounting & Finance',
'version': '0.1',
'depends': ['account_bank_statement_import'],
'data': [
],
}

1
beesdoo_coda/models/__init__.py

@ -0,0 +1 @@
import bank_statement

12
beesdoo_coda/models/bank_statement.py

@ -0,0 +1,12 @@
# -*- coding: utf-8 -*-
'''
Created on 16 mai 2016
@author: Thibault Francois (thibault@françois.be)
'''
from openerp import models, fields
class BankStatement(models.Model):
_inherit = 'account.bank.statement'
coda_note = fields.Text()

6
beesdoo_coda/wizard/__init__.py

@ -0,0 +1,6 @@
'''
Created on 19 mai 2016
@author: mythrys
'''
import import_coda

77
beesdoo_coda/wizard/import_coda.py

@ -0,0 +1,77 @@
# -*- coding: utf-8 -*-
'''
Created on 16 mai 2016
@author: Thibault Francois (thibault@françois.be)
'''
from coda.parser import Parser
from openerp import models, _
class CodaBankStatementImport(models.TransientModel):
_inherit = 'account.bank.statement.import'
def _generate_note(self, move):
notes = []
if move.counterparty_name:
notes.append("%s: %s" % (_('Counter Party Name'), move.counterparty_name))
if move.counterparty_address:
notes.append("%s: %s" % (_('Counter Party Address'), move.counterparty_address))
if move.counterparty_number:
notes.append("%s: %s" % (_('Counter Party Account'), move.counterparty_number))
if move.communication:
notes.append("%s: %s" % (_('Communication'), move.communication))
return '\n'.join(notes)
def _get_move_value(self, move, statement, sequence):
move_data = {
'name': move.communication, #ok
'note': self._generate_note(move),
'date': move.entry_date, #ok
'amount': move.transaction_amount if move.transaction_amount_sign == '0' else - move.transaction_amount, #ok
'account_number': move.counterparty_number, #ok
'partner_name': move.counterparty_name, #ok
'ref': move.ref,
'sequence': sequence, #ok
'unique_import_id' : statement.coda_seq_number + '-' + statement.old_balance_date + '-' + statement.new_balance_date + '-' + move.ref
}
return move_data
def _get_statement_data(self, statement):
statement_data = {
'name' : statement.paper_seq_number,
'date' : statement.creation_date,
'balance_start': statement.old_balance, #ok
'balance_end_real' : statement.new_balance, #ok
'coda_note' : '',
'transactions' : []
}
return statement_data
def _get_acc_number(self, acc_number):
#Check if we match the exact acc_number or the end of an acc number
journal = self.env['account.journal'].search([('bank_acc_number', '=like', '%' + acc_number)])
if not journal or len(journal) > 1: #if not found or ambiguious
return acc_number
return journal.bank_acc_number
def _parse_file(self, data_file):
parser = Parser()
try:
statements = parser.parse(data_file)
except ValueError:
return super(CodaBankStatementImport, self)._parse_file(data_file)
currency_code = False
account_number = False
stmts_vals = []
for statement in statements:
account_number = statement.acc_number
currency_code = statement.currency
statement_data = self._get_statement_data(statement)
stmts_vals.append(statement_data)
for move in statement.movements:
statement_data['transactions'].append(self._get_move_value(move, statement, len(statement_data['transactions']) + 1))
return currency_code, self._get_acc_number(account_number), stmts_vals

22
beesdoo_migration_asbl_to_coop/__init__.py

@ -0,0 +1,22 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2010 Openerp sa (<http://openerp.com>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
import migration

40
beesdoo_migration_asbl_to_coop/__openerp__.py

@ -0,0 +1,40 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
{
'name': 'Import data from ASBL',
'version': '0.9',
'category': 'Import',
'description': """
This module provide a tools to import data from ASBL
""",
'author': 'Thibault Francois',
'website': 'https://github.com/tfrancoi/',
'depends': ['base', 'import_base', 'import_odoo'],
'data': [
'view/migration.xml'
],
'test': [], #TODO provide test
'installable': True,
'auto_install': False,
}
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

220
beesdoo_migration_asbl_to_coop/migration.py

@ -0,0 +1,220 @@
# -*- coding: utf-8 -*-
from openerp import models, fields, api
from openerp.exceptions import Warning
from openerp.addons.import_base.import_framework import *
from openerp.addons.import_base.mapper import *
class odoo_connection_data(models.TransientModel):
_name = 'beesdoo.import.asbl'
@api.multi
def migrate(self):
imp = migration_framework(self, self.env.cr, self.env.uid, "Odoo", 'beesdoo.import.asbl', dict(self.env.context))
imp.launch_import()
class migration_framework(import_framework):
black_list_field = {
}
tables = ['product.category',
'product.uom',
'product.uom.categ',
'pos.category',
'res.partner',
'product.template',
'product.supplierinfo',
]
table_domain = {
'res.partner' : [('supplier', '=', True), '|', ('active', '=', True), ('active', '=', False)],
'product.template' : ['|', ('active', '=', True), ('active', '=', False)]
}
def initialize(self):
self.connection = self.obj.env['import.odoo.connection'].search([], limit=1)
self.set_table_list(self.tables)
print self.connection.name
def _get_field(self, model):
fields = ['id']
for mapper_object in self.get_mapping()[model.model_name]['map'].values():
if isinstance(mapper_object, basestring):
fields.append(mapper_object)
else:
fields.extend(mapper_object.get_fields())
print "read field", fields
return fields
def res_to_dict(self, fields, datas):
datas = datas['datas']
res = []
for data in datas:
data_dict = {}
for i, field in enumerate(fields):
data_dict[field] = data[i]
res.append(data_dict)
return res
def get_data(self, table):
con = self.connection._get_connection()
obj = con.get_model(table)
fields = self._get_field(obj)
ids = obj.search(self.table_domain.get(table, []))
datas = obj.export_data(ids, fields, context={'lang' : 'fr_BE'})
return self.res_to_dict(fields, datas)
def _generate_xml_id(self, name, table):
"""
@param name: name of the object, has to be unique in for a given table
@param table : table where the record we want generate come from
@return: a unique xml id for record, the xml_id will be the same given the same table and same name
To be used to avoid duplication of data that don't have ids
"""
return name
def get_mapping(self):
return {
'product.category': {
'model' : 'product.category',
'dependencies' : [],
'map' : {
'name' : 'name',
'parent_id/id_parent' : 'parent_id/id',
'type' : 'type',
}
},
'product.uom.categ' : {
'model' : 'product.uom.categ',
'dependencies' : [],
'map' : {
'name' : 'name',
}
},
'product.uom': {
'model' : 'product.uom',
'dependencies' : ['product.uom.categ'],
'map' : {
'name' : 'name',
'category_id/id' : 'category_id/id',
'rounding' : 'rounding',
'uom_type' : 'uom_type',
'factor' : 'factor',
'factor_inv' : 'factor_inv',
}
},
'pos.category': {
'model' : 'pos.category',
'dependencies' : [],
'map' : {
'id' : 'id',
'name' : 'name',
'parent_id/id_parent' : 'parent_id/id',
}
},
'res.partner': {
'model' : 'res.partner',
'dependencies' : [],
'map' : {
'active' : 'active',
'barcode' : 'barcode',
'birthdate' : 'birthdate',
'city' : 'city',
'comment' : 'comment',
'company_type' : 'company_type',
'contact_address' : 'contact_address',
'country_id/id' : 'country_id/id',
'email' : 'email',
'employee' : 'employee',
'fax' : 'fax',
'first_name' : 'first_name',
'function' : 'function',
'is_company' : 'is_company',
'lang' : 'lang',
'last_name' : 'last_name',
'mobile' : 'mobile',
'name' : 'name',
'parent_id/id_parent' : 'parent_id/id',
'phone' : 'phone',
'ref' : 'ref',
'street' : 'street',
'street2' : 'street2',
'supplier' : 'supplier',
'website' : 'website',
'zip' : 'zip',
'supplier' : 'supplier',
'customer' : 'customer',
'vat' : 'vat',
}
},
'beesdoo.product.label' : {
'model' : 'beesdoo.product.label',
'dependencies' : [],
'map' : {
'color_code' : 'color_code',
'name' : 'name',
'type' : 'type',
}
},
'product.template': {
'model' : 'product.template',
'dependencies' : ['pos.category', 'product.category', 'beesdoo.product.label'],
'map' : {
'active' : 'active',
'available_in_pos' : 'available_in_pos',
'barcode' : 'barcode',
'categ_id/id' : 'categ_id/id',
'default_code' : 'default_code',
'description' : 'description',
'description_picking' : 'description_picking',
'description_purchase' : 'description_purchase',
'description_sale' : 'description_sale',
'eco_label/id' : 'eco_label/id',
'fair_label/id' : 'fair_label/id',
'invoice_policy' : 'invoice_policy',
'local_label/id' : 'local_label/id',
'name' : 'name',
'origin_label/id' : 'origin_label/id',
'pos_categ_id/id' : 'pos_categ_id/id',
'purchase_ok' : 'purchase_ok',
'sale_delay' : 'sale_delay',
'sale_ok' : 'sale_ok',
'standard_price' : 'standard_price',
'supplier_taxes_id/id' : 'supplier_taxes_id/id', #Taxes problème
'taxes_id/id' : 'taxes_id/id',
'to_weight' : 'to_weight',
'type' : 'type',
'uom_id/id' : 'uom_id/id',
'uom_po_id/id' : 'uom_po_id/id',
'weight' : 'weight',
'list_price' : 'list_price',
}
},
'product.supplierinfo': {
'model' : 'product.supplierinfo',
'dependencies' : ['product.template'],
'map' : {
'delay' : 'delay',
'min_qty' : 'min_qty',
'name/id' : 'name/id',
'price' : 'price',
'product_code' : 'product_code',
'product_name' : 'product_name',
'product_uom/id' : 'product_uom/id',
'date_start' : 'date_start',
'date_end' : 'date_end',
'product_tmpl_id/id': 'product_tmpl_id/id',
}
},
}

31
beesdoo_migration_asbl_to_coop/view/migration.xml

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<record model="ir.ui.view" id="import_odoo_form">
<field name="name">beesdoo.import.asbl.form</field>
<field name="model">beesdoo.import.asbl</field>
<field name="arch" type="xml">
<form string="Migration" version="7.0">
<footer>
<button type="object" name="migrate" string="Migrate" />
or
<button special="cancel" name="cancel" string="Cancel" />
</footer>
</form>
</field>
</record>
<record model="ir.actions.act_window" id="odoo_import_form">
<field name="name">Import From ASBL</field>
<field name="res_model">beesdoo.import.asbl</field>
<field name="view_type">form</field>
<field name="view_mode">form</field>
<field name="target">new</field>
</record>
<menuitem id="perso_account_migration_menu"
parent="import_odoo.import_odoo"
name="Import data from ASBL"
action="odoo_import_form" />
</data>
</openerp>

4
beesdoo_pos/__openerp__.py

@ -4,7 +4,7 @@
'summary': """
Module that extends the pos for the beescoop
""",
""",
'description': """
Long description of module's purpose
@ -20,7 +20,7 @@
'version': '0.1',
# any module necessary for this one to work correctly
'depends': ['point_of_sale', 'beesdoo_base'],
'depends': ['beesdoo_base', 'beesdoo_product'],
# always loaded
'data': [

6
beesdoo_pos/data/email.xml

@ -8,9 +8,9 @@
<field name="email_from">${(object.user_id.email and '%s &lt;%s&gt;' % (object.user_id.name, object.user_id.email) or '')|safe}</field>
<field name="subject">${object.pos_reference}</field>
<field name="partner_to">${object.partner_id.id}</field>
<field name="model_id" ref="point_of_sale.model_pos_order"/>
<field name="auto_delete" eval="True"/>
<field name="report_template" ref="point_of_sale.action_report_pos_receipt"/>
<field name="model_id" ref="point_of_sale.model_pos_order" />
<field name="auto_delete" eval="True" />
<field name="report_template" ref="point_of_sale.action_report_pos_receipt" />
<field name="report_name">Ticket ${object.pos_reference}</field>
<field name="lang">${object.partner_id.lang}</field>
<field name="body_html"><![CDATA[

27
beesdoo_pos/models/beesdoo_pos.py

@ -36,6 +36,29 @@ class BeescoopPosOrder(models.Model):
return _('Error: no order found')
if not order.partner_id.email:
return _('Cannot send the ticket, no email address found on the client')
mail_template = self.env.ref("beescoop_pos.email_send_ticket")
mail_template = self.env.ref("beesdoo_pos.email_send_ticket")
mail_template.send_mail(order.id)
return _("Ticket sent")
return _("Ticket sent")
class BeescoopPosPartner(models.Model):
_inherit = 'res.partner'
def _get_eater(self):
eater1, eater2 = False, False
if self.child_eater_ids:
eater1 = self.child_eater_ids[0].name
if len(self.child_eater_ids) > 1:
eater2 = self.child_eater_ids[1].name
return eater1, eater2
@api.multi
def get_balance_and_eater(self):
self.ensure_one()
account_id = self.property_account_receivable_id.id
move_lines = self.env['account.move.line'].search([('account_id', '=', account_id), ('partner_id', '=', self.id)])
credit = sum([m.credit for m in move_lines])
debit = sum([m.debit for m in move_lines])
eater1, eater2 = self._get_eater()
return str(round(credit - debit, 2)), eater1, eater2
last_name = fields.Char('Last Name', required=True, default="/")

24
beesdoo_pos/static/src/css/beesdoo.css

@ -1,4 +1,28 @@
.message-send {
margin: 16px;
text-align: center;
}
.customer-information {
margin: 16px 24px 16px 24px;
font-weight: bold;
font-size: 16px;
}
.customer-name {
font-size: 18px;
}
.button.set-customer.decentered {
height: 108px;
}
.customer-information-pay {
font-weight: normal;
font-size: 12px;
text-align: left;
}
.pos .actionpad .button.pay {
height: 108px;
}

63
beesdoo_pos/static/src/js/beesdoo.js

@ -2,12 +2,23 @@ odoo.define('beescoop.pos', function (require) {
"use strict";
var module = require("point_of_sale.screens");
var Model = require('web.DataModel');
var set_customer_info = function(el_class, value, prefix) {
var el = this.$(el_class);
el.empty();
if (prefix && value) {
value = prefix + value
}
if (value) {
el.append(value);
}
}
module.ReceiptScreenWidget = module.ReceiptScreenWidget.include({
send : function() {
var self = this;
var loaded = new $.Deferred();
var order = this.pos.get_order().name;
var records = new Model('pos.order').call('send_order', [order], {});
var records = new Model('pos.order').call('send_order', [order], {}, { shadow: false, timeout: 10000});
records.then(function(result){
var el = self.$('.message-send')
el.empty();
@ -31,4 +42,54 @@ odoo.define('beescoop.pos', function (require) {
this.$('.message-send').empty();
},
})
module.ActionpadWidget = module.ActionpadWidget.include({
renderElement : function() {
var self = this;
var loaded = new $.Deferred();
this._super();
if (!this.pos.get_client()) {
return
}
var customer_id = this.pos.get_client().id;
var res = new Model('res.partner').call('get_balance_and_eater',
[ customer_id ], undefined, { shadow: true, timeout: 1000});
res.then(function(result) {
set_customer_info.call(self, '.customer-balance', result[0])
set_customer_info.call(self, '.customer-delegate1', result[1], 'Eater 1: ');
set_customer_info.call(self, '.customer-delegate2', result[2], 'Eater 2: ');
}, function(err) {
loaded.reject(err);
});
},
});
module.PaymentScreenWidget.include({
render_customer_info : function() {
var self = this;
var loaded = new $.Deferred();
if (!this.pos.get_client()) {
return
}
var customer_id = this.pos.get_client().id;
var res = new Model('res.partner').call('get_balance_and_eater', [ customer_id ], undefined, { shadow: true, timeout: 1000});
res.then(function(result) {
set_customer_info.call(self, '.customer-name', self.pos.get_client().name);
set_customer_info.call(self, '.customer-balance', result[0]);
set_customer_info.call(self, '.customer-delegate1', result[1], 'Eater 1: ');
set_customer_info.call(self, '.customer-delegate2', result[2], 'Eater 2: ');
}, function(err) {
loaded.reject(err);
});
},
renderElement : function() {
this._super();
this.render_customer_info();
},
customer_changed : function() {
this._super();
this.render_customer_info();
},
});
});

39
beesdoo_pos/static/src/xml/templates.xml

@ -3,11 +3,46 @@
<t t-extend="ReceiptScreenWidget">
<t t-jquery='.pos-receipt-container' t-operation='before'>
<div class="button send">
<i class='fa fa-envelope'></i> Send Receipt By Mail
<i class='fa fa-envelope'></i>
Send Receipt By Mail
</div>
<div class="message-send">
</div>
</t>
</t>
<t t-extend="ActionpadWidget">
<t t-jquery="t[t-if='widget.pos.get_client()']" t-operation="after">
<t t-if="widget.pos.get_client()">
<div class='customer-information-pay'>
Balance:
<span class='customer-balance' />
<br />
<span class='customer-delegate1' />
<br />
<span class='customer-delegate2' />
</div>
</t>
</t>
</t>
<t t-extend="PaymentScreenWidget">
<t t-jquery=".paymentmethods-container" t-operation="inner">
<t t-if="widget.pos.get_client()">
<div class="customer-information">
<span class='customer-name' />
<br />
Balance:
<span class='customer-balance' />
<br />
<span class='customer-delegate1' />
<br />
<span class='customer-delegate2' />
<br />
</div>
</t>
</t>
</t>
</templates>

24
beesdoo_pos/views/beesdoo_pos.xml

@ -7,7 +7,7 @@
<field name="arch" type="xml">
<field name="iface_tax_included" position="after">
<separator string="Bill Value" colspan="2" />
<field name="bill_value" nolabel="1" colspan="2" >
<field name="bill_value" nolabel="1" colspan="2">
<tree editable="bottom">
<field name="name" />
</tree>
@ -16,11 +16,23 @@
</field>
</record>
<record id="view_account_bnk_stmt_cashbox" model="ir.ui.view">
<field name="name">account.bnk_stmt_cashbox.form</field>
<field name="model">account.bank.statement.cashbox</field>
<field name="inherit_id" ref="account.view_account_bnk_stmt_cashbox" />
<field name="arch" type="xml">
<field name="coin_value" position="attributes">
<attribute name="readonly">1</attribute>
</field>
</field>
</record>
<template id="assets" inherit_id="point_of_sale.assets">
<xpath expr="." position="inside">
<script type="text/javascript" src="/beesdoo_pos/static/src/js/beesdoo.js"></script>
<link rel='stylesheet' href="/beesdoo_pos/static/src/css/beesdoo.css"/>
</xpath>
<xpath expr="." position="inside">
<script type="text/javascript"
src="/beesdoo_pos/static/src/js/beesdoo.js"></script>
<link rel='stylesheet' href="/beesdoo_pos/static/src/css/beesdoo.css" />
</xpath>
</template>
</data>
</openerp>
</openerp>

3
beesdoo_product/__init__.py

@ -1,2 +1,3 @@
# -*- coding: utf-8 -*-
import models
import models
import wizard

5
beesdoo_product/__openerp__.py

@ -20,14 +20,15 @@
'version': '0.1',
# any module necessary for this one to work correctly
'depends': ['beesdoo_base', 'product'],
'depends': ['beesdoo_base', 'product', 'point_of_sale'],
# always loaded
'data': [
'data/product_label.xml',
'views/beesdoo_product.xml',
'wizard/views/label_printing_utils.xml',
'security/ir.model.access.csv',
],
# only loaded in demonstration mode
'demo': [],
}
}

4
beesdoo_product/data/product_label.xml

@ -61,5 +61,9 @@
<field name="type">delivery</field>
<field name="color_code">#ff4000</field>
</record>
<record id="consignes_group_tax" model="account.tax.group">
<field name="name">Consignes</field>
<field name="sequence" eval="10" />
</record>
</data>
</odoo>

45
beesdoo_product/models/beesdoo_product.py

@ -1,5 +1,7 @@
# -*- coding: utf-8 -*-
from openerp import models, fields, api
from openerp.tools.translate import _
from openerp.exceptions import UserError
class BeesdooProduct(models.Model):
_inherit = "product.template"
@ -9,6 +11,49 @@ class BeesdooProduct(models.Model):
fair_label = fields.Many2one('beesdoo.product.label', domain = [('type', '=', 'fair')])
origin_label = fields.Many2one('beesdoo.product.label', domain = [('type', '=', 'delivery')])
main_seller_id = fields.Many2one('res.partner', compute='_compute_main_seller_id', store=True)
display_unit = fields.Many2one('product.uom')
default_reference_unit = fields.Many2one('product.uom')
display_weight = fields.Float(compute='_get_display_weight', store=True)
total_with_vat = fields.Float(compute='_get_total', store=True, string="Total Sales Price with VAT")
total_with_vat_by_unit = fields.Float(compute='_get_total', store=True, string="Total Sales Price with VAT by Reference Unit")
total_deposit = fields.Float(compute='_get_total', store=True, string="Deposit Price")
label_to_be_printed = fields.Boolean('Print label?')
label_last_printed = fields.Datetime('Label last printed on')
@api.one
@api.depends('seller_ids', 'seller_ids.date_start')
def _compute_main_seller_id(self):
# Calcule le vendeur associé qui a la date de début la plus récente et plus petite qu’aujourd’hui
sellers_ids = self.seller_ids.sorted(key=lambda seller: seller.date_start, reverse=True)
self.main_seller_id = sellers_ids and sellers_ids[0].name or False
@api.one
@api.depends('taxes_id', 'list_price', 'taxes_id.amount', 'taxes_id.tax_group_id', 'total_with_vat', 'display_weight', 'weight')
def _get_total(self):
consignes_group = self.env.ref('beesdoo_product.consignes_group_tax', raise_if_not_found=False)
tax_amount_sum = sum([tax._compute_amount(self.list_price, self.list_price) for tax in self.taxes_id if tax.tax_group_id != consignes_group])
self.total_deposit = sum([tax._compute_amount(self.list_price, self.list_price) for tax in self.taxes_id if tax.tax_group_id == consignes_group])
self.total_with_vat = self.list_price + tax_amount_sum
if self.display_weight > 0:
self.total_with_vat_by_unit = self.total_with_vat / self.weight
@api.one
@api.depends('weight', 'display_unit')
def _get_display_weight(self):
self.display_weight = self.weight * self.display_unit.factor
@api.one
@api.constrains('display_unit', 'default_reference_unit')
def _unit_same_category(self):
if self.display_unit.category_id != self.default_reference_unit.category_id:
raise UserError(_('Reference Unit and Display Unit should belong to the same category'))
class BeesdooProductLabel(models.Model):
_name = "beesdoo.product.label"

49
beesdoo_product/views/beesdoo_product.xml

@ -1,19 +1,56 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record model="ir.ui.view" id="beesdoo_product_form">
<field name="name">bees.product.template.form</field>
<field name="model">product.template</field>
<field name="inherit_id" ref="product.product_template_only_form_view" />
<field name="arch" type="xml">
<field name="barcode" position="after">
<field name="eco_label" />
<field name="local_label" />
<field name="fair_label" />
<field name="origin_label" />
</field>
<field name="invoice_policy" position="attributes">
<attribute name="invisible">1</attribute>
</field>
<field name="purchase_method" position="attributes">
<attribute name="invisible">1</attribute>
</field>
<field name="route_ids" position="attributes">
<attribute name="invisible">1</attribute>
</field>
<group name="sale_condition" position="attributes">
<attribute name="invisible">1</attribute>
</group>
<field name="property_account_creditor_price_difference" position="attributes">
<attribute name="invisible">1</attribute>
</field>
<xpath expr="//group[@name='inventory']/.." position="after">
<page string="Label">
<group>
<group name="label">
<field name="total_with_vat"/>
<field name="display_weight"/>
<field name="display_unit" />
<field name="default_reference_unit"/>
<field name="total_with_vat_by_unit" />
<field name="total_deposit" />
</group>
<group>
<field name="main_seller_id" />
<field name="eco_label"/>
<field name="local_label"/>
<field name="fair_label"/>
<field name="origin_label"/>
<field name="label_to_be_printed"/>
<field name="label_last_printed"/>
</group>
</group>
</page>
</xpath>
</field>
</record>
<record model="ir.ui.view" id="beesdoo_product_label_form">
<field name="name">bees.product.label.form</field>
<field name="model">beesdoo.product.label</field>

1
beesdoo_product/wizard/__init__.py

@ -0,0 +1 @@
import label_printing_utils

21
beesdoo_product/wizard/label_printing_utils.py

@ -0,0 +1,21 @@
# -*- coding: utf-8 -*-
from openerp import models, fields, api
class RequestLabelPrintingWizard(models.TransientModel):
_name = 'label.printing.wizard'
def _get_selected_products(self):
return self.env.context['active_ids']
product_ids = fields.Many2many('product.template', default=_get_selected_products)
@api.one
def request_printing(self):
self.product_ids.write({'label_to_be_printed' : True})
@api.one
def set_as_printed(self):
self.product_ids.write({'label_to_be_printed' : False, 'label_last_printed' : fields.Datetime.now()})

55
beesdoo_product/wizard/views/label_printing_utils.xml

@ -0,0 +1,55 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="printing_label_request_wizard" model="ir.ui.view">
<field name="name">Request Label Printing Wizard</field>
<field name="model">label.printing.wizard</field>
<field name="arch" type="xml">
<form>
<field name="product_ids" />
<footer>
<button
type="object"
name="request_printing"
string="Demander l'impression d'un label"
class="oe_highlight" />
<button special="cancel" string="Annuler" />
</footer>
</form>
</field>
</record>
<act_window name="Request label printing"
res_model="label.printing.wizard"
src_model="product.template"
view_mode="form"
target="new"
view_id="printing_label_request_wizard"
key2="client_action_multi"
id="beesdoo_product_action_request_label_printing"
/>
<record id="set_label_as_printed_wizard" model="ir.ui.view">
<field name="name">Request Label Printing Wizard</field>
<field name="model">label.printing.wizard</field>
<field name="arch" type="xml">
<form>
<field name="product_ids" />
<footer>
<button
type="object"
name="set_as_printed"
string="Marquer les labels comme imprimés"
class="oe_highlight" />
<button special="cancel" string="Annuler" />
</footer>
</form>
</field>
</record>
<act_window name="Set label as printed"
res_model="label.printing.wizard"
src_model="product.template"
view_mode="form"
view_id="set_label_as_printed_wizard"
target="new"
key2="client_action_multi"
id="beesdoo_product_action_set_label_as_printed"
/>
</odoo>

4
beesdoo_project/__openerp__.py

@ -1,9 +1,9 @@
# -*- coding: utf-8 -*-
{
'name': "Beescoop Project Management",
'name': "Beescoop Project Management",
'summary': """
Module that customize project management for Beescoop
Module that customize project management for Beescoop
""",
'description': """

11
beesdoo_project/models/task.py

@ -8,14 +8,13 @@
from openerp import models, fields, api
class Task(models.Model):
_inherit = 'project.task'
author_id = fields.Many2one('res.users', string="Author")
reviewer_id = fields.Many2one('res.users', string="Reviewer")
tester_id = fields.Many2one('res.users', string="Tester")
link_task_ids = fields.Many2many('project.task',
relation="link_task_relation_table",
column1='user1_id',
link_task_ids = fields.Many2many('project.task',
relation="link_task_relation_table",
column1='user1_id',
column2='user2_id', string="Linked Tasks")

2
beesdoo_purchase/__init__.py

@ -0,0 +1,2 @@
# -*- coding: utf-8 -*-
import models

31
beesdoo_purchase/__openerp__.py

@ -0,0 +1,31 @@
# -*- coding: utf-8 -*-
{
'name': "Bees Purchase",
'summary': """
Extension du module Purchase""",
'description': """
Long description of module's purpose
""",
'author': "Beescoop - Cellule IT",
'website': "https://github.com/beescoop/Obeesdoo",
# Categories can be used to filter modules in modules listing
# Check https://github.com/odoo/odoo/blob/master/openerp/addons/base/module/module_data.xml
# for the full list
'category': 'Purchase',
'version': '0.1',
# any module necessary for this one to work correctly
'depends': ['purchase','beesdoo_product'],
# always loaded
'data': [
'views/purchase_order.xml',
'security/ir.model.access.csv',
],
# only loaded in demonstration mode
'demo': [],
}

2
beesdoo_purchase/models/__init__.py

@ -0,0 +1,2 @@
# -*- coding: utf-8 -*-

1
beesdoo_purchase/security/ir.model.access.csv

@ -0,0 +1 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink

14
beesdoo_purchase/views/purchase_order.xml

@ -0,0 +1,14 @@
<openerp>
<data>
<record model="ir.ui.view" id="beesdoo_purchase_order_form_view">
<field name="name">beesdoo.purchase.order.form.view</field>
<field name="model">purchase.order</field>
<field name="inherit_id" ref="purchase.purchase_order_form" />
<field name="arch" type="xml">
<field name="product_id" position="attributes">
<attribute name="domain">[('main_seller_id','=', parent.partner_id)]</attribute>
</field>
</field>
</record>
</data>
</openerp>

25
import_base/__init__.py

@ -0,0 +1,25 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2010 Openerp sa (<http://openerp.com>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
import import_framework
import mapper
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

42
import_base/__openerp__.py

@ -0,0 +1,42 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
{
'name': 'Framework for complex import',
'version': '0.9',
'category': 'Hidden/Dependency',
'description': """
This module provide a class import_framework to help importing
complex data from other software
""",
'author': 'OpenERP SA',
'website': 'http://www.openerp.com',
'depends': ['base'],
'init_xml': [],
'update_xml': [],
'demo_xml': [],
'test': [], #TODO provide test
'installable': True,
'auto_install': False,
'certificate': '00141537995453',
}
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

415
import_base/import_framework.py

@ -0,0 +1,415 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
import pprint
import mapper
from openerp.tools.translate import _
import datetime
import logging
import StringIO
import traceback
pp = pprint.PrettyPrinter(indent=4)
class import_framework():
"""
This class should be extends,
get_data and get_mapping have to extends
get_state_map and initialize can be extended
for advanced purpose get_default_hook can also be extended
@see dummy import for a minimal exemple
"""
"""
for import_object, this domain will avoid to find an already existing object
"""
DO_NOT_FIND_DOMAIN = [('id', '=', 0)]
#TODO don't use context to pass credential parameters
def __init__(self, obj, cr, uid, instance_name, module_name, context=None):
self.external_id_field = 'id'
self.obj = obj
self.cr = cr
self.uid = uid
self.instance_name = instance_name
self.module_name = module_name
self.context = context or {}
self.table_list = []
self.logger = logging.getLogger(module_name)
self.initialize()
"""
Abstract Method to be implemented in
the real instance
"""
def initialize(self):
"""
init before import
usually for the login
"""
pass
def init_run(self):
"""
call after intialize run in the thread, not in the main process
TO use for long initialization operation
"""
pass
def get_data(self, table):
"""
@return: a list of dictionaries
each dictionnaries contains the list of pair external_field_name : value
"""
return [{}]
def get_link(self, from_table, ids, to_table):
"""
@return: a dictionaries that contains the association between the id (from_table)
and the list (to table) of id linked
"""
return {}
def get_external_id(self, data):
"""
@return the external id
the default implementation return self.external_id_field (that has 'id') by default
if the name of id field is different, you can overwrite this method or change the value
of self.external_id_field
"""
return data[self.external_id_field]
def get_mapping(self):
"""
@return: { TABLE_NAME : {
'model' : 'openerp.model.name',
#if true import the table if not just resolve dependencies, use for meta package, by default => True
#Not required
'import' : True or False,
#Not required
'dependencies' : [TABLE_1, TABLE_2],
#Not required
'hook' : self.function_name, #get the val dict of the object, return the same val dict or False
'map' : { @see mapper
'openerp_field_name' : 'external_field_name', or val('external_field_name')
'openerp_field_id/id' : ref(TABLE_1, 'external_id_field'), #make the mapping between the external id and the xml on the right
'openerp_field2_id/id_parent' : ref(TABLE_1,'external_id_field') #indicate a self dependencies on openerp_field2_id
'state' : map_val('state_equivalent_field', mapping), # use get_state_map to make the mapping between the value of the field and the value of the state
'text_field' : concat('field_1', 'field_2', .., delimiter=':'), #concat the value of the list of field in one
'description_field' : ppconcat('field_1', 'field_2', .., delimiter='\n\t'), #same as above but with a prettier formatting
'field' : call(callable, arg1, arg2, ..), #call the function with all the value, the function should send the value : self.callable
'field' : callable
'field' : call(method, val('external_field') interface of method is self, val where val is the value of the field
'field' : const(value) #always set this field to value
+ any custom mapper that you will define
}
},
}
"""
return {}
def default_hook(self, val):
"""
this hook will be apply on each table that don't have hook
here we define the identity hook
"""
return val
def _import_table(self, table):
self.logger.info('Import table %s' % table)
data = self.get_data(table)
map = self.get_mapping()[table]['map']
hook = self.get_mapping()[table].get('hook', self.default_hook)
model = self.get_mapping()[table]['model']
final_data = []
for val in data:
res = hook(val)
if res:
final_data.append(res)
return self._save_data(model, dict(map), final_data, table)
def _save_data(self, model, mapping, datas, table):
"""
@param model: the model of the object to import
@param table : the external table where the data come from
@param mapping : definition of the mapping
@see: get_mapping
@param datas : list of dictionnaries
datas = [data_1, data_2, ..]
data_i is a map external field_name => value
and each data_i have a external id => in data_id['id']
"""
self.logger.info(' Importing %s into %s' % (table, model))
if not datas:
return (0, 'No data found')
mapping['id'] = 'id_new'
res = []
self_dependencies = []
for k in mapping.keys():
if '_parent' in k:
self_dependencies.append((k[:-7], mapping.pop(k)))
for data in datas:
for k, field_name in self_dependencies:
data[k] = data.get(field_name) and self._generate_xml_id(data.get(field_name), table)
data['id_new'] = self._generate_xml_id(self.get_external_id(data), table)
fields, values = self._fields_mapp(data, mapping, table)
res.append(values)
model_obj = self.obj.pool.get(model)
if not model_obj:
raise ValueError(_("%s is not a valid model name") % model)
self.logger.info(_("fields imported : ") + str(fields))
(p, r, warning, s) = model_obj.import_data(self.cr, self.uid, fields, res, mode='update', current_module=self.module_name, noupdate=False, context=self.context)
self.logger.info('%s %s %s %s %s' % ("Done", p, r, warning, s))
for (field, field_name) in self_dependencies:
self.logger.info('Import parent %s' % field)
self._import_self_dependencies(model_obj, field, datas)
return (len(res), warning)
def _import_self_dependencies(self, obj, parent_field, datas):
"""
@param parent_field: the name of the field that generate a self_dependencies, we call the object referenced in this
field the parent of the object
@param datas: a list of dictionnaries
Dictionnaries need to contains
id_new : the xml_id of the object
field_new : the xml_id of the parent
"""
fields = ['id', parent_field]
for data in datas:
if data.get(parent_field):
values = [data['id_new'], data[parent_field]]
res = obj.import_data(self.cr, self.uid, fields, [values], mode='update', current_module=self.module_name, noupdate=False, context=self.context)
def _preprocess_mapping(self, mapping):
"""
Preprocess the mapping :
after the preprocces, everything is
callable in the val of the dictionary
use to allow syntaxical sugar like 'field': 'external_field'
instead of 'field' : value('external_field')
"""
map = dict(mapping)
for key, value in map.items():
if isinstance(value, basestring):
map[key] = mapper.value(value)
#set parent for instance of dbmapper
elif isinstance(value, mapper.dbmapper):
value.set_parent(self)
return map
def _fields_mapp(self,dict_sugar, openerp_dict, table):
"""
call all the mapper and transform data
to be compatible with import_data
"""
fields=[]
data_lst = []
mapping = self._preprocess_mapping(openerp_dict)
for key,val in mapping.items():
if key not in fields and dict_sugar:
fields.append(key)
value = val(dict(dict_sugar))
data_lst.append(value)
return fields, data_lst
def _generate_xml_id(self, name, table):
"""
@param name: name of the object, has to be unique in for a given table
@param table : table where the record we want generate come from
@return: a unique xml id for record, the xml_id will be the same given the same table and same name
To be used to avoid duplication of data that don't have ids
"""
sugar_instance = self.instance_name
name = name.replace('.', '_').replace(',', '_')
return sugar_instance + "_" + table + "_" + name
"""
Public interface of the framework
those function can be use in the callable function defined in the mapping
"""
def xml_id_exist(self, table, external_id):
"""
Check if the external id exist in the openerp database
in order to check if the id exist the table where it come from
should be provide
@return the xml_id generated if the external_id exist in the database or false
"""
if not external_id:
return False
xml_id = self._generate_xml_id(external_id, table)
id = self.obj.pool.get('ir.model.data').search(self.cr, self.uid, [('name', '=', xml_id), ('module', '=', self.module_name)])
return id and xml_id or False
def name_exist(self, table, name, model):
"""
Check if the object with the name exist in the openerp database
in order to check if the id exist the table where it come from
should be provide and the model of the object
"""
fields = ['name']
data = [name]
return self.import_object(fields, data, model, table, name, [('name', '=', name)])
def get_mapped_id(self, table, external_id, context=None):
"""
@return return the databse id linked with the external_id
"""
if not external_id:
return False
xml_id = self._generate_xml_id(external_id, table)
return self.obj.pool.get('ir.model.data').get_object_reference(self.cr, self.uid, self.module_name, xml_id)[1]
def import_object_mapping(self, mapping, data, model, table, name, domain_search=False):
"""
same as import_objects but instead of two list fields and data,
this method take a dictionnaries : external_field : value
and the mapping similar to the one define in 'map' key
@see import_object, get_mapping
"""
fields, datas = self._fields_mapp(data, mapping, table)
return self.import_object(fields, datas, model, table, name, domain_search)
def import_object(self, fields, data, model, table, name, domain_search=False):
"""
This method will import an object in the openerp, usefull for field that is only a char in sugar and is an object in openerp
use import_data that will take care to create/update or do nothing with the data
this method return the xml_id
To be use, when you want to create an object or link if already exist
use DO_NOT_LINK_DOMAIN to create always a new object
@param fields: list of fields needed to create the object without id
@param data: the list of the data, in the same order as the field
ex : fields = ['firstname', 'lastname'] ; data = ['John', 'Mc donalds']
@param model: the openerp's model of the create/update object
@param table: the table where data come from in sugarcrm, no need to fit the real name of openerp name, just need to be unique
@param unique_name: the name of the object that we want to create/update get the id
@param domain_search : the domain that should find the unique existing record
@return: the xml_id of the ressources
"""
domain_search = not domain_search and [('name', 'ilike', name)] or domain_search
obj = self.obj.pool.get(model)
if not obj: #if the model doesn't exist
return False
xml_id = self._generate_xml_id(name, table)
xml_ref = self.mapped_id_if_exist(model, domain_search, table, name)
fields.append('id')
data.append(xml_id)
obj.import_data(self.cr, self.uid, fields, [data], mode='update', current_module=self.module_name, noupdate=True, context=self.context)
return xml_ref or xml_id
def mapped_id_if_exist(self, model, domain, table, name):
"""
To be use when we want link with and existing object, if the object don't exist
just ignore.
@param domain : search domain to find existing record, should return a unique record
@param xml_id: xml_id give to the mapping
@param name: external_id or name of the object to create if there is no id
@param table: the name of the table of the object to map
@return : the xml_id if the record exist in the db, False otherwise
"""
obj = self.obj.pool.get(model)
ids = obj.search(self.cr, self.uid, domain, context=self.context)
if ids:
xml_id = self._generate_xml_id(name, table)
ir_model_data_obj = obj.pool.get('ir.model.data')
id = ir_model_data_obj._update(self.cr, self.uid, model,
self.module_name, {}, mode='update', xml_id=xml_id,
noupdate=True, res_id=ids[0], context=self.context)
return xml_id
return False
def set_table_list(self, table_list):
"""
Set the list of table to import, this method should be call before run
@param table_list: the list of external table to import
['Leads', 'Opportunity']
"""
self.table_list = table_list
def launch_import(self):
"""
Import all data into openerp,
this is the Entry point to launch the process of import
"""
self.data_started = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
error = False
result = []
try:
self.init_run()
imported = set() #to invoid importing 2 times the sames modules
for table in self.table_list:
to_import = self.get_mapping()[table].get('import', True)
if not table in imported:
res = self._resolve_dependencies(self.get_mapping()[table].get('dependencies', []), imported)
result.extend(res)
if to_import:
(position, warning) = self._import_table(table)
result.append((table, position, warning))
imported.add(table)
self.cr.commit()
except Exception, err:
sh = StringIO.StringIO()
traceback.print_exc(file=sh)
error = sh.getvalue()
self.logger.error(error)
self.date_ended = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
def _resolve_dependencies(self, dep, imported):
"""
import dependencies recursively
and avoid to import twice the same table
"""
result = []
for dependency in dep:
if not dependency in imported:
to_import = self.get_mapping()[dependency].get('import', True)
res = self._resolve_dependencies(self.get_mapping()[dependency].get('dependencies', []), imported)
result.extend(res)
if to_import:
r = self._import_table(dependency)
(position, warning) = r
result.append((dependency, position, warning))
imported.add(dependency)
return result
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

220
import_base/mapper.py

@ -0,0 +1,220 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from openerp import tools
class mapper(object):
"""
super class for all mapper class
They are call before import data
to transform the mapping into real value that we
will import
the call function receive a dictionary with external data
'external_field' : value
"""
def __call__(self, external_values):
raise NotImplementedError()
def get_fields(self):
return []
class dbmapper(mapper):
"""
Super class for mapper that need to access to
data base or any function of the import_framework
self.parent contains a reference to the instance of
the import framework
"""
def set_parent(self, parent):
self.parent = parent
class concat(mapper):
"""
Use : contact('field_name1', 'field_name2', delimiter='_')
concat value of fields using the delimiter, delimiter is optional
and by default is a space
"""
def __init__(self, *arg, **delimiter):
self.arg = arg
self.delimiter = delimiter and delimiter.get('delimiter', ' ') or ' '
def __call__(self, external_values):
return self.delimiter.join(map(lambda x : tools.ustr(external_values.get(x,'')), self.arg))
def get_fields(self):
return self.arg
class ppconcat(concat):
"""
Use : contact('field_name1', 'field_name2', delimiter='_')
concat external field name and value of fields using the delimiter,
delimiter is optional and by default is a two line feeds
"""
def __init__(self, *arg, **delimiter):
self.arg = arg
self.delimiter = delimiter and delimiter.get('delimiter', ' ') or '\n\n'
def __call__(self, external_values):
return self.delimiter.join(map(lambda x : x + ": " + tools.ustr(external_values.get(x,'')), self.arg))
class const(mapper):
"""
Use : const(arg)
return always arg
"""
def __init__(self, val):
self.val = val
def __call__(self, external_values):
return self.val
class value(mapper):
"""
Use : value(external_field_name)
Return the value of the external field name
this is equivalent to the a single string
usefull for call if you want your call get the value
and don't care about the name of the field
call(self.method, value('field1'))
"""
def __init__(self, val, default='', fallback=False):
self.val = val
self.default = default
self.fallback = fallback
def __call__(self, external_values):
val = external_values.get(self.val, self.default)
if self.fallback and (not val or val == self.default):
val = external_values.get(self.fallback, self.default)
return val
def get_fields(self):
return [self.val]
class map_val(mapper):
"""
Use : map_val(external_field, val_mapping)
where val_mapping is a dictionary
with external_val : openerp_val
usefull for selection field like state
to map value
"""
def __init__(self, val, map, default='draft'):
self.val = value(val)
self.map = map
self.default = default
def __call__(self, external_values):
return self.map.get(self.val(external_values), self.default)
def get_fields(self):
return self.val.get_fields()
class map_val_default(mapper):
"""
Use : map_val(external_field, val_mapping)
where val_mapping is a dictionary
with external_val : openerp_val
usefull for selection field like state
to map value
"""
def __init__(self, val, map):
self.val = value(val)
self.map = map
def __call__(self, external_values):
value = self.map.get(self.val(external_values), self.val(external_values))
return value
def get_fields(self):
return self.val.get_fields()
class ref(dbmapper):
"""
Use : ref(table_name, external_id)
return the xml_id of the ressource
to associate an already imported object with the current object
"""
def __init__(self, table, field_name):
self.table = table
self.field_name = field_name
def __call__(self, external_values):
return self.parent.xml_id_exist(self.table, external_values.get(self.field_name))
def get_fields(self):
return self.field_name
class refbyname(dbmapper):
"""
Use : refbyname(table_name, external_name, res.model)
same as ref but use the name of the ressource to find it
"""
def __init__(self, table, field_name, model):
self.table = table
self.field_name = field_name
self.model = model
def __call__(self, external_values):
v = external_values.get(self.field_name, '')
return self.parent.name_exist(self.table, v , self.model)
def get_fields(self):
return self.field_name
class call(mapper):
"""
Use : call(function, arg1, arg2)
to call the function with external val follow by the arg specified
"""
def __init__(self, fun, *arg):
self.fun = fun
self.arg = arg
def __call__(self, external_values):
args = []
for arg in self.arg:
if isinstance(arg, mapper):
args.append(arg(external_values))
else:
args.append(arg)
return self.fun(external_values, *args)
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
def val(field_name):
def val_fun(data_line):
return data_line[field_name]
return val_fun
def const(const):
def val_fun(data_line):
return const

23
import_odoo/__init__.py

@ -0,0 +1,23 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2010 Openerp sa (<http://openerp.com>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
import odoo_connector
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

41
import_odoo/__openerp__.py

@ -0,0 +1,41 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
{
'name': 'Framework to import from other odoo instance',
'version': '0.9',
'category': 'Import',
'description': """
This module provide a class import_framework to help importing
data from odoo
""",
'author': 'Thibault Francois',
'website': 'https://github.com/tfrancoi/',
'depends': ['base', 'import_base'],
'data': [
'view/connector.xml'
],
'test': [], #TODO provide test
'installable': True,
'auto_install': False,
}
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

38
import_odoo/odoo_connector.py

@ -0,0 +1,38 @@
'''
Created on 25 nov. 2014
@author: openerp
'''
from openerp import models, fields, api
import openerplib
from openerp.exceptions import Warning
class odoo_connection_data(models.Model):
_name = 'import.odoo.connection'
name = fields.Char("Name", required=True)
host = fields.Char("Host", required=True)
port = fields.Integer("Port", required=True, default=8069)
database = fields.Char("Database", required=True)
user = fields.Char("Login", required=True, default="admin")
password = fields.Char("Password", required=True)
protocol = fields.Selection([('xmlrpc', 'Xmlrpc'), ('jsonrpc', 'Jsonrpc'),('xmlrpcs', 'Xmlrpcs'), ('jsonrpcs', 'Jsonrpcs')], string="Protocol", default="xmlrpc")
active = fields.Boolean("Active", default=True)
@api.multi
def test_connection(self):
connection = self._get_connection()
connection.check_login(force=True)
raise Warning("Connection Successful")
def _get_connection(self):
return openerplib.get_connection(hostname=self.host,
port=self.port,
database=self.database,
login=self.user,
password=self.password,
protocol=self.protocol)

67
import_odoo/view/connector.xml

@ -0,0 +1,67 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<record model="ir.ui.view" id="import_odoo_tree">
<field name="name">import.odoo.connection.tree</field>
<field name="model">import.odoo.connection</field>
<field name="arch" type="xml">
<tree string="Odoo Connector">
<field name="name"/>
<field name="host"/>
<field name="database" />
</tree>
</field>
</record>
<record model="ir.ui.view" id="import_odoo_form">
<field name="name">import.odoo.connection.form</field>
<field name="model">import.odoo.connection</field>
<field name="arch" type="xml">
<form string="Account" version="7.0">
<header>
<button type="object" name="test_connection" string="Test Connection" />
</header>
<sheet>
<div class="oe_title">
<div class="oe_edit_only">
<label for="name"/>
</div>
<h1>
<field name="name" default_focus="1" placeholder="Name" />
</h1>
</div>
<group>
<group>
<field name="host" />
<field name="port" />
<field name="database" />
<field name="protocol" />
</group>
<group>
<field name="user" />
<field name="password" />
<field name="active" />
</group>
</group>
</sheet>
</form>
</field>
</record>
<record model="ir.actions.act_window" id="odoo_import_form">
<field name="name">Odoo Connector</field>
<field name="res_model">import.odoo.connection</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
</record>
<menuitem id="import_odoo" name="Import from odoo" parent="base.menu_custom" />
<menuitem id="odoo_import_form_menu" parent="import_odoo" name="Odoo Connector" action="odoo_import_form"/>
</data>
</openerp>

69
web_environment_ribbon/README.rst

@ -0,0 +1,69 @@
.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg
:alt: License: AGPL-3
Web Environment Ribbon
======================
Mark a Test Environment with a red ribbon on the top left corner in every page
.. image:: /web_environment_ribbon/static/description/screenshot.png
:alt: Screenshot
Installation
============
* No special setup
Configuration
=============
* You can change the ribbon's name ("TEST") by editing
the default system parameter "ribbon.name"
(in the menu Settings > Parameters > System Parameters)
To hide the ribbon, set this parameter to "False" or
delete it.
Usage
=====
To use this module, you need only to install it. After installation, a red
ribbon will be visible on top left corner of every Odoo backend page
Known issues / Roadmap
======================
* Allow to define in some place (system parameter, configuration file...) the
ribbon color.
Bug Tracker
===========
Bugs are tracked on `GitHub Issues <https://github.com/OCA/web/issues>`_.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us smashing it by providing a detailed and welcomed feedback
`here <https://github.com/OCA/web/issues/new?body=module:%20web_environment_ribbon%0Aversion:%208.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
Credits
=======
Contributors
------------
* Francesco Apruzzese <cescoap@gmail.com>
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 http://odoo-community.org.

20
web_environment_ribbon/__init__.py

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Copyright (C) 2015 Francesco OpenCode Apruzzese (<cescoap@gmail.com>)
# All Rights Reserved
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################

40
web_environment_ribbon/__openerp__.py

@ -0,0 +1,40 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Copyright (C) 2015 Francesco OpenCode Apruzzese (<cescoap@gmail.com>)
# All Rights Reserved
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
{
'name': "Web Environment Ribbon",
'version': '8.0.0.1.0',
'category': 'Web',
'author': 'Francesco OpenCode Apruzzese,Odoo Community Association (OCA),Thibault Francois',
'website': 'https://it.linkedin.com/in/francescoapruzzese',
'license': 'AGPL-3',
"depends": [
'web',
],
"data": [
'view/base_view.xml',
'data/ribbon_data.xml',
],
"update_xml": [],
"demo_xml": [],
"auto_install": False,
'installable': True,
}

12
web_environment_ribbon/data/ribbon_data.xml

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data noupdate="1">
<!-- Add ribbon name default configuration parameter -->
<record id="default_ribbon_name" model="ir.config_parameter">
<field name="key">ribbon.name</field>
<field name="value">TEST</field>
</record>
</data>
</openerp>

BIN
web_environment_ribbon/static/description/icon.png

After

Width: 128  |  Height: 128  |  Size: 9.1 KiB

BIN
web_environment_ribbon/static/description/screenshot.png

After

Width: 607  |  Height: 198  |  Size: 43 KiB

24
web_environment_ribbon/static/src/css/ribbon.css

@ -0,0 +1,24 @@
.test-ribbon{
width: 200px;
background: #e43;
position: absolute;
top: 50px;
left: -50px;
text-align: center;
line-height: 50px;
letter-spacing: 1px;
color: #f0f0f0;
-webkit-transform: rotate(-45deg);
-ms-transform: rotate(-45deg);
-moz-transform: rotate(-45deg);
-o-transform: rotate(-45deg);
transform: rotate(-45deg);
z-index: 1000;
position: fixed;
box-shadow: 0 0 3px rgba(0,0,0,.3);
background: rgba(255, 0, 0, .6);;
}
.test-ribbon b {
font-size: 20px;
}

35
web_environment_ribbon/static/src/js/ribbon.js

@ -0,0 +1,35 @@
/******************************************************************************
Copyright (C) 2015 Akretion (http://www.akretion.com)
@author Sylvain Calador <sylvain.calador@akretion.com>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
openerp.web_environment_ribbon = function(instance) {
var ribbon = $(document).find('.test-ribbon');
ribbon.hide();
var model = new instance.web.Model('ir.config_parameter');
var key = 'ribbon.name';
var res = model.call('get_param', [key]).then(
function (name) {
if (name && name != 'False') {
ribbon.html(name);
ribbon.show();
}
}
);
}

21
web_environment_ribbon/view/base_view.xml

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<!-- Load css for ribbons -->
<template id="assets_backend" name="ribbon_test assets" inherit_id="web.assets_backend">
<xpath expr="." position="inside">
<link rel="stylesheet" href="/web_environment_ribbon/static/src/css/ribbon.css"/>
<script type="text/javascript" src="/web_environment_ribbon/static/src/js/ribbon.js"/>
</xpath>
</template>
<!-- Add ribbon to page -->
<template id="body_with_ribbon_test" name="ribbon_test web.webclient_bootstrap" inherit_id="web.webclient_bootstrap">
<xpath expr="//div[@class='openerp openerp_webclient_container oe_webclient']" position="before">
<div class="test-ribbon"/>
</xpath>
</template>
</data>
</openerp>
Loading…
Cancel
Save