[MERGE] latest trunk

bzr revid: abo@openerp.com-20120822175756-ypym270e6cklpbds
This commit is contained in:
Antonin Bourguignon 2012-08-22 19:57:56 +02:00
commit 121b351b59
183 changed files with 6911 additions and 59071 deletions

View File

@ -7,14 +7,14 @@ msgstr ""
"Project-Id-Version: OpenERP Server 6.0dev\n" "Project-Id-Version: OpenERP Server 6.0dev\n"
"Report-Msgid-Bugs-To: support@openerp.com\n" "Report-Msgid-Bugs-To: support@openerp.com\n"
"POT-Creation-Date: 2012-02-08 00:35+0000\n" "POT-Creation-Date: 2012-02-08 00:35+0000\n"
"PO-Revision-Date: 2012-05-10 17:32+0000\n" "PO-Revision-Date: 2012-08-17 11:07+0000\n"
"Last-Translator: Michael Otcheskih <otma@mail.ru>\n" "Last-Translator: Chertykov Denis <chertykov@gmail.com>\n"
"Language-Team: \n" "Language-Team: \n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2012-08-07 05:06+0000\n" "X-Launchpad-Export-Date: 2012-08-18 04:58+0000\n"
"X-Generator: Launchpad (build 15745)\n" "X-Generator: Launchpad (build 15810)\n"
#. module: account #. module: account
#: view:account.invoice.report:0 #: view:account.invoice.report:0
@ -9872,7 +9872,7 @@ msgstr "Не определен счет доходов для ТМЦ: \"%s\" (i
#. module: account #. module: account
#: constraint:account.move.line:0 #: constraint:account.move.line:0
msgid "You can not create journal items on closed account." msgid "You can not create journal items on closed account."
msgstr "" msgstr "Нельзя создать элемент журнала по закрытому счету ."
#. module: account #. module: account
#: field:account.account,unrealized_gain_loss:0 #: field:account.account,unrealized_gain_loss:0

View File

@ -7,7 +7,7 @@
currency_id: base.EUR currency_id: base.EUR
invoice_line: invoice_line:
- account_id: account.a_sale - account_id: account.a_sale
name: '[PC1] Basic PC' name: '[PCSC234] PC Assemble SC234'
price_unit: 450.0 price_unit: 450.0
quantity: 1.0 quantity: 1.0
product_id: product.product_product_3 product_id: product.product_product_3

View File

@ -7,7 +7,7 @@
currency_id: base.EUR currency_id: base.EUR
invoice_line: invoice_line:
- account_id: account.a_sale - account_id: account.a_sale
name: '[PC1] Basic PC' name: '[PCSC234] PC Assemble SC234'
price_unit: 450.0 price_unit: 450.0
quantity: 1.0 quantity: 1.0
product_id: product.product_product_3 product_id: product.product_product_3

View File

@ -20,7 +20,7 @@
currency_id: base.EUR currency_id: base.EUR
invoice_line: invoice_line:
- account_id: account.a_sale - account_id: account.a_sale
name: '[PC3] Medium PC' name: '[PC-DEM] PC on Demand'
price_unit: 900.0 price_unit: 900.0
quantity: 10.0 quantity: 10.0
product_id: product.product_product_5 product_id: product.product_product_5

View File

@ -28,7 +28,7 @@
currency_id: base.EUR currency_id: base.EUR
invoice_line: invoice_line:
- account_id: account.a_expense - account_id: account.a_expense
name: '[PC1] Basic PC' name: '[PCSC234] PC Assemble SC234'
price_unit: 300.0 price_unit: 300.0
product_id: product.product_product_3 product_id: product.product_product_3
quantity: 10.0 quantity: 10.0

View File

@ -24,7 +24,7 @@
uos_id: 1 uos_id: 1
quantity: 5.0 quantity: 5.0
price_unit: 100.0 price_unit: 100.0
name: 'Medium PC' name: 'PC on Demand'
account_id: account.a_pay account_id: account.a_pay
tax_line: tax_line:
- name: sale tax - name: sale tax
@ -104,9 +104,9 @@
"__model": "account.invoice.line", "__model": "account.invoice.line",
"__id": "account:b22acf7a-ddcd-11e0-a4db-701a04e25543.account_invoice_line-1RP3so", "__id": "account:b22acf7a-ddcd-11e0-a4db-701a04e25543.account_invoice_line-1RP3so",
"uos_id": ["product:b22acf7a-ddcd-11e0-a4db-701a04e25543.product_uom_unit", "Unit"], "uos_id": ["product:b22acf7a-ddcd-11e0-a4db-701a04e25543.product_uom_unit", "Unit"],
"name": "Basic PC", "name": "PC Assemble SC234",
"price_unit": 10.0, "price_unit": 10.0,
"product_id": ["product:b22acf7a-ddcd-11e0-a4db-701a04e25543.product_product_3", "[PC1] Basic PC"], "product_id": ["product:b22acf7a-ddcd-11e0-a4db-701a04e25543.product_product_3", "[PCSC234] PC Assemble SC234"],
"quantity": 1.0 "quantity": 1.0
}, },
{ {
@ -114,9 +114,9 @@
"__model": "account.invoice.line", "__model": "account.invoice.line",
"__id": "account:b22acf7a-ddcd-11e0-a4db-701a04e25543.account_invoice_line-u2XV5", "__id": "account:b22acf7a-ddcd-11e0-a4db-701a04e25543.account_invoice_line-u2XV5",
"uos_id": ["product:b22acf7a-ddcd-11e0-a4db-701a04e25543.product_uom_unit", "Unit"], "uos_id": ["product:b22acf7a-ddcd-11e0-a4db-701a04e25543.product_uom_unit", "Unit"],
"name": "Medium PC", "name": "PC on Demand",
"price_unit": 100.0, "price_unit": 100.0,
"product_id": ["product:b22acf7a-ddcd-11e0-a4db-701a04e25543.product_product_5", "[PC3] Medium PC"], "product_id": ["product:b22acf7a-ddcd-11e0-a4db-701a04e25543.product_product_5", "[PC-DEM] PC on Demand"],
"quantity": 5.0 "quantity": 5.0
}] }]
} }
@ -137,12 +137,12 @@
assert invoice_new.type == 'in_invoice', "Invoice type was not set properly" assert invoice_new.type == 'in_invoice', "Invoice type was not set properly"
assert len(invoice_new.invoice_line) == 2, "invoice lines are not same" assert len(invoice_new.invoice_line) == 2, "invoice lines are not same"
for inv_line in invoice_new.invoice_line: for inv_line in invoice_new.invoice_line:
if inv_line.name == 'Basic PC': if inv_line.name == 'PC Assemble SC234':
assert inv_line.uos_id.name == "Unit" , "uom is not same" assert inv_line.uos_id.name == "Unit" , "uom is not same"
assert inv_line.price_unit == 10 , "price unit is not same" assert inv_line.price_unit == 10 , "price unit is not same"
assert inv_line.quantity == 1 , "product qty is not same" assert inv_line.quantity == 1 , "product qty is not same"
assert inv_line.price_subtotal == 10, "price sub total is not same" assert inv_line.price_subtotal == 10, "price sub total is not same"
elif inv_line.name == 'Medium PC': elif inv_line.name == 'PC on Demand':
assert inv_line.uos_id.name == "Unit" , "uom is not same" assert inv_line.uos_id.name == "Unit" , "uom is not same"
assert inv_line.price_unit == 100 , "price unit is not same" assert inv_line.price_unit == 100 , "price unit is not same"
assert inv_line.quantity == 5 , "product qty is not same" assert inv_line.quantity == 5 , "product qty is not same"

View File

@ -1,10 +1,10 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<openerp> <openerp>
<data> <data>
<!-- rename root menu "Accounting" --> <!-- Rename root menu "Accounting" -->
<record id="account.menu_finance" model="ir.ui.menu"> <!-- Top menu item -->
<field name="name">Accounting</field> <menuitem name="Accounting"
</record> id="account.menu_finance"/>
</data> </data>
<data noupdate="1"> <data noupdate="1">
@ -19,10 +19,8 @@
<field name="type">automatic</field> <field name="type">automatic</field>
<field name="sequence">100</field> <field name="sequence">100</field>
</record> </record>
</data>
<data noupdate="1"> <!-- Notify all employees of module installation -->
<!-- notify all employees of module installation -->
<function model="mail.group" name="message_append_note"> <function model="mail.group" name="message_append_note">
<!-- ids, subject, body, parent_id=False, type='notification', content_subtype='html' --> <!-- ids, subject, body, parent_id=False, type='notification', content_subtype='html' -->
<value eval="[ref('mail.group_all_employees')]"/> <value eval="[ref('mail.group_all_employees')]"/>

View File

@ -7,14 +7,14 @@ msgstr ""
"Project-Id-Version: OpenERP Server 6.0dev\n" "Project-Id-Version: OpenERP Server 6.0dev\n"
"Report-Msgid-Bugs-To: support@openerp.com\n" "Report-Msgid-Bugs-To: support@openerp.com\n"
"POT-Creation-Date: 2012-02-08 00:35+0000\n" "POT-Creation-Date: 2012-02-08 00:35+0000\n"
"PO-Revision-Date: 2010-12-23 13:10+0000\n" "PO-Revision-Date: 2012-08-17 10:54+0000\n"
"Last-Translator: Chertykov Denis <chertykov@gmail.com>\n" "Last-Translator: Chertykov Denis <chertykov@gmail.com>\n"
"Language-Team: \n" "Language-Team: \n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2012-08-07 05:17+0000\n" "X-Launchpad-Export-Date: 2012-08-18 04:58+0000\n"
"X-Generator: Launchpad (build 15745)\n" "X-Generator: Launchpad (build 15810)\n"
#. module: account_analytic_default #. module: account_analytic_default
#: help:account.analytic.default,partner_id:0 #: help:account.analytic.default,partner_id:0
@ -133,7 +133,7 @@ msgstr "Аналитика по умолчанию"
#. module: account_analytic_default #. module: account_analytic_default
#: sql_constraint:stock.picking:0 #: sql_constraint:stock.picking:0
msgid "Reference must be unique per Company!" msgid "Reference must be unique per Company!"
msgstr "" msgstr "Ссылка должна быть уникальна для каждой компании!"
#. module: account_analytic_default #. module: account_analytic_default
#: view:account.analytic.default:0 #: view:account.analytic.default:0

View File

@ -1106,7 +1106,8 @@ class account_voucher(osv.osv):
# otherwise we use the rates of the system (giving the voucher date in the context) # otherwise we use the rates of the system (giving the voucher date in the context)
amount_currency = currency_obj.compute(cr, uid, company_currency, line.move_line_id.currency_id.id, move_line['debit']-move_line['credit'], context=ctx) amount_currency = currency_obj.compute(cr, uid, company_currency, line.move_line_id.currency_id.id, move_line['debit']-move_line['credit'], context=ctx)
if line.amount == line.amount_unreconciled and line.move_line_id.currency_id.id == voucher_currency: if line.amount == line.amount_unreconciled and line.move_line_id.currency_id.id == voucher_currency:
foreign_currency_diff = line.move_line_id.amount_residual_currency + amount_currency sign = voucher_brw.type in ('payment', 'purchase') and -1 or 1
foreign_currency_diff = sign * line.move_line_id.amount_residual_currency + amount_currency
move_line['amount_currency'] = amount_currency move_line['amount_currency'] = amount_currency
voucher_line = move_line_obj.create(cr, uid, move_line) voucher_line = move_line_obj.create(cr, uid, move_line)

View File

@ -54,7 +54,7 @@
currency_id: base.EUR currency_id: base.EUR
journal_id: account.bank_journal journal_id: account.bank_journal
name: Voucher Axelor name: Voucher Axelor
narration: Basic PC narration: PC Assemble SC234
line_dr_ids: line_dr_ids:
- account_id: account.cash - account_id: account.cash
amount: 1000.0 amount: 1000.0

View File

@ -6,7 +6,7 @@
company_id: base.main_company company_id: base.main_company
journal_id: account.bank_journal journal_id: account.bank_journal
name: Voucher Axelor name: Voucher Axelor
narration: Basic PC narration: PC Assemble SC234
amount: 1000.0 amount: 1000.0
line_ids: line_ids:
- account_id: account.cash - account_id: account.cash

View File

@ -79,7 +79,7 @@
period_id: account.period_1 period_id: account.period_1
invoice_line: invoice_line:
- account_id: account.a_sale - account_id: account.a_sale
name: '[PC1] Basic PC' name: '[PCSC234] PC Assemble SC234'
price_unit: 200.0 price_unit: 200.0
quantity: 1.0 quantity: 1.0
product_id: product.product_product_3 product_id: product.product_product_3
@ -112,7 +112,7 @@
period_id: account.period_2 period_id: account.period_2
invoice_line: invoice_line:
- account_id: account.a_sale - account_id: account.a_sale
name: '[PC1] Basic PC' name: '[PCSC234] PC Assemble SC234'
price_unit: 100.0 price_unit: 100.0
quantity: 1.0 quantity: 1.0
product_id: product.product_product_3 product_id: product.product_product_3

View File

@ -50,7 +50,7 @@
period_id: account.period_1 period_id: account.period_1
invoice_line: invoice_line:
- account_id: account.a_expense - account_id: account.a_expense
name: '[PC1] Basic PC' name: '[PCSC234] PC Assemble SC234'
price_unit: 200.0 price_unit: 200.0
quantity: 1.0 quantity: 1.0
product_id: product.product_product_3 product_id: product.product_product_3
@ -85,7 +85,7 @@
type : in_invoice type : in_invoice
invoice_line: invoice_line:
- account_id: account.a_expense - account_id: account.a_expense
name: '[PC1] Basic PC' name: '[PCSC234] PC Assemble SC234'
price_unit: 100.0 price_unit: 100.0
quantity: 1.0 quantity: 1.0
product_id: product.product_product_3 product_id: product.product_product_3

View File

@ -85,7 +85,7 @@
period_id: account.period_1 period_id: account.period_1
invoice_line: invoice_line:
- account_id: account.a_sale - account_id: account.a_sale
name: '[PC1] Basic PC' name: '[PCSC234] PC Assemble SC234'
price_unit: 200.0 price_unit: 200.0
quantity: 1.0 quantity: 1.0
product_id: product.product_product_3 product_id: product.product_product_3
@ -118,7 +118,7 @@
period_id: account.period_2 period_id: account.period_2
invoice_line: invoice_line:
- account_id: account.a_sale - account_id: account.a_sale
name: '[PC1] Basic PC' name: '[PCSC234] PC Assemble SC234'
price_unit: 100.0 price_unit: 100.0
quantity: 1.0 quantity: 1.0
product_id: product.product_product_3 product_id: product.product_product_3

View File

@ -85,7 +85,7 @@
period_id: account.period_1 period_id: account.period_1
invoice_line: invoice_line:
- account_id: account.a_sale - account_id: account.a_sale
name: '[PC1] Basic PC' name: '[PCSC234] PC Assemble SC234'
price_unit: 200.0 price_unit: 200.0
quantity: 1.0 quantity: 1.0
product_id: product.product_product_3 product_id: product.product_product_3
@ -118,7 +118,7 @@
period_id: account.period_2 period_id: account.period_2
invoice_line: invoice_line:
- account_id: account.a_sale - account_id: account.a_sale
name: '[PC1] Basic PC' name: '[PCSC234] PC Assemble SC234'
price_unit: 100.0 price_unit: 100.0
quantity: 1.0 quantity: 1.0
product_id: product.product_product_3 product_id: product.product_product_3

View File

@ -39,7 +39,7 @@
period_id: account.period_1 period_id: account.period_1
invoice_line: invoice_line:
- account_id: account.a_sale - account_id: account.a_sale
name: '[PC1] Basic PC' name: '[PCSC234] PC Assemble SC234'
price_unit: 150.0 price_unit: 150.0
quantity: 1.0 quantity: 1.0
product_id: product.product_product_3 product_id: product.product_product_3
@ -72,7 +72,7 @@
period_id: account.period_1 period_id: account.period_1
invoice_line: invoice_line:
- account_id: account.a_sale - account_id: account.a_sale
name: '[PC1] Basic PC' name: '[PCSC234] PC Assemble SC234'
price_unit: 80.0 price_unit: 80.0
quantity: 1.0 quantity: 1.0
product_id: product.product_product_3 product_id: product.product_product_3

View File

@ -73,7 +73,7 @@
period_id: account.period_1 period_id: account.period_1
invoice_line: invoice_line:
- account_id: account.a_sale - account_id: account.a_sale
name: '[PC1] Basic PC' name: '[PCSC234] PC Assemble SC234'
price_unit: 200.0 price_unit: 200.0
quantity: 1.0 quantity: 1.0
product_id: product.product_product_3 product_id: product.product_product_3

View File

@ -52,7 +52,7 @@
period_id: account.period_1 period_id: account.period_1
invoice_line: invoice_line:
- account_id: account.a_sale - account_id: account.a_sale
name: '[PC1] Basic PC' name: '[PCSC234] PC Assemble SC234'
price_unit: 1000.0 price_unit: 1000.0
quantity: 1.0 quantity: 1.0
product_id: product.product_product_3 product_id: product.product_product_3

View File

@ -7,7 +7,7 @@
currency_id: base.EUR currency_id: base.EUR
invoice_line: invoice_line:
- account_id: account.a_sale - account_id: account.a_sale
name: '[PC1] Basic PC' name: '[PCSC234] PC Assemble SC234'
price_unit: 450.0 price_unit: 450.0
quantity: 1.0 quantity: 1.0
product_id: product.product_product_3 product_id: product.product_product_3

View File

@ -1,3 +1,4 @@
import controllers import controllers
import auth_oauth import auth_oauth
import res_users import res_users
import res_config

View File

@ -31,9 +31,10 @@ Allow users to login through Google OAuth2.
'author': 'Victor Tabuenca', 'author': 'Victor Tabuenca',
'maintainer': 'OpenERP s.a.', 'maintainer': 'OpenERP s.a.',
'website': 'http://www.openerp.com', 'website': 'http://www.openerp.com',
'depends': ['base', 'web'], 'depends': ['base', 'web', 'base_setup'],
'data': ['auth_oauth_data.xml', 'data': [
'auth_oauth_view.xml' 'auth_oauth_data.xml',
'auth_oauth_view.xml'
], ],
'js': ['static/src/js/auth_oauth.js'], 'js': ['static/src/js/auth_oauth.js'],
'css': ['static/lib/zocial/css/zocial.css'], 'css': ['static/lib/zocial/css/zocial.css'],

View File

@ -1,6 +1,6 @@
from openerp.osv import osv, fields from openerp.osv import osv, fields
class auth_oauth_providers(osv.osv): class auth_oauth_provider(osv.osv):
"""Class defining the configuration values of an OAuth2 provider""" """Class defining the configuration values of an OAuth2 provider"""
_name = 'auth.oauth.provider' _name = 'auth.oauth.provider'
@ -8,15 +8,17 @@ class auth_oauth_providers(osv.osv):
_order = 'name' _order = 'name'
_columns = { _columns = {
'name' : fields.char('Provider name', required=True), # Name of the OAuth2 entity, Google, LinkedIn, etc 'name' : fields.char('Provider name'), # Name of the OAuth2 entity, Google, LinkedIn, etc
'client_id' : fields.char('Client ID', required=True), # Our identifier 'client_id' : fields.char('Client ID'), # Our identifier
'auth_endpoint' : fields.char('Authentication URL', required=True), # OAuth provider URL to authenticate users 'auth_endpoint' : fields.char('Authentication URL'), # OAuth provider URL to authenticate users
'scope' : fields.char('Scope'), # OAUth user data desired to access 'scope' : fields.char('Scope'), # OAUth user data desired to access
'validation_endpoint' : fields.char('Validation URL'), # OAuth provider URL to validate tokens 'validation_endpoint' : fields.char('Validation URL'), # OAuth provider URL to validate tokens
'data_endpoint' : fields.char('Data URL'), 'data_endpoint' : fields.char('Data URL'),
'enabled' : fields.boolean('Allowed'),
'css_class' : fields.char('CSS class'), 'css_class' : fields.char('CSS class'),
'body' : fields.char('Body'), 'body' : fields.char('Body'),
'active' : fields.boolean('Active'),
'sequence' : fields.integer(), 'sequence' : fields.integer(),
} }
_defaults = {
'enabled' : False,
}

View File

@ -1,38 +1,34 @@
<?xml version="1.0"?> <?xml version="1.0"?>
<openerp> <openerp>
<data noupdate="1"> <data>
<record id="provider_facebook" model="auth.oauth.provider"> <record id="provider_facebook" model="auth.oauth.provider">
<field name="name">Facebook Graph</field> <field name="name">Facebook Graph</field>
<field name="client_id">facebook_client_id</field>
<field name="auth_endpoint">https://www.facebook.com/dialog/oauth</field> <field name="auth_endpoint">https://www.facebook.com/dialog/oauth</field>
<field name="scope"></field> <field name="scope"></field>
<field name="validation_endpoint">https://graph.facebook.com/me/permissions</field> <field name="validation_endpoint">https://graph.facebook.com/me/permissions</field>
<field name="data_endpoint"></field> <field name="data_endpoint"></field>
<field name="css_class">zocial facebook</field> <field name="css_class">zocial facebook</field>
<field name="body">Sign in with facebook</field> <field name="body">Sign in with facebook</field>
<field name="active">True</field>
</record> </record>
<record id="provider_google" model="auth.oauth.provider"> <record id="provider_google" model="auth.oauth.provider">
<field name="name">Google OAuth2</field> <field name="name">Google OAuth2</field>
<field name="client_id">108010644258-duuhmp6pu7li4tsmnqg7j9rvdeklg0ki.apps.googleusercontent.com</field>
<field name="auth_endpoint">https://accounts.google.com/o/oauth2/auth</field> <field name="auth_endpoint">https://accounts.google.com/o/oauth2/auth</field>
<field name="scope">https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile</field> <field name="scope">https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile</field>
<field name="validation_endpoint">https://www.googleapis.com/oauth2/v1/tokeninfo</field> <field name="validation_endpoint">https://www.googleapis.com/oauth2/v1/tokeninfo</field>
<field name="data_endpoint">https://www.googleapis.com/oauth2/v1/userinfo</field> <field name="data_endpoint">https://www.googleapis.com/oauth2/v1/userinfo</field>
<field name="css_class">zocial google</field> <field name="css_class">zocial google</field>
<field name="body">Sign in with google</field> <field name="body">Sign in with google</field>
<field name="active">True</field>
</record> </record>
<record id="provider_twitter" model="auth.oauth.provider"> <!-- <record id="provider_twitter" model="auth.oauth.provider">
<field name="name">Twitter OAuth2</field> <field name="name">Twitter OAuth</field>
<field name="client_id">108010644258-duuhmp6pu7li4tsmnqg7j9rvdeklg0ki.apps.twitterusercontent.com</field>
<field name="auth_endpoint">https://api.twitter.com/oauth/request_token</field> <field name="auth_endpoint">https://api.twitter.com/oauth/request_token</field>
<field name="scope"></field> <field name="scope"></field>
<field name="validation_endpoint">https://api.twitter.com/oauth/authorize</field> <field name="validation_endpoint">https://api.twitter.com/oauth/authorize</field>
<field name="data_endpoint"></field> <field name="data_endpoint"></field>
<field name="css_class">zocial twitter</field> <field name="css_class">zocial twitter</field>
<field name="body">Sign in with twitter</field> <field name="body">Sign in with twitter</field>
<field name="active">True</field> </record> -->
</record>
</data> </data>
</openerp> </openerp>

View File

@ -11,7 +11,7 @@
<group> <group>
<field name="name" /> <field name="name" />
<field name="client_id" /> <field name="client_id" />
<field name="active" /> <field name="enabled" />
</group> </group>
<group> <group>
<field name="auth_endpoint" /> <field name="auth_endpoint" />
@ -22,7 +22,7 @@
</sheet> </sheet>
</form> </form>
</field> </field>
</record> </record>
<record model="ir.ui.view" id="view_oauth_provider_list"> <record model="ir.ui.view" id="view_oauth_provider_list">
<field name="name">auth.oauth.provider.list</field> <field name="name">auth.oauth.provider.list</field>
<field name="model">auth.oauth.provider</field> <field name="model">auth.oauth.provider</field>
@ -31,7 +31,7 @@
<tree string="arch" version="7.0"> <tree string="arch" version="7.0">
<field name="name" /> <field name="name" />
<field name="client_id" /> <field name="client_id" />
<field name="active" /> <field name="enabled" />
</tree> </tree>
</field> </field>
</record> </record>

View File

@ -18,7 +18,7 @@ class OAuthController(openerpweb.Controller):
registry = openerp.modules.registry.RegistryManager.get(dbname) registry = openerp.modules.registry.RegistryManager.get(dbname)
with registry.cursor() as cr: with registry.cursor() as cr:
providers = registry.get('auth.oauth.provider') providers = registry.get('auth.oauth.provider')
l = providers.read(cr, 1, providers.search(cr, 1, [])) l = providers.read(cr, 1, providers.search(cr, 1, [('enabled','=',True)]))
return l return l
@openerpweb.httprequest @openerpweb.httprequest

View File

@ -0,0 +1,63 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2012-Today OpenERP SA (<http://www.openerp.com>)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>
#
##############################################################################
from openerp.osv import osv, fields
import logging
_logger = logging.getLogger(__name__)
class base_config_settings(osv.TransientModel):
_inherit = 'base.config.settings'
_columns = {
'auth_oauth_google_enabled' : fields.boolean('Allow users to sign in with Google'),
'auth_oauth_google_client_id' : fields.char('Client ID'),
'auth_oauth_facebook_enabled' : fields.boolean('Allow users to sign in with Facebook'),
'auth_oauth_facebook_client_id' : fields.char('Client ID'),
}
def get_oauth_providers(self, cr, uid, fields, context=None):
google_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'auth_oauth', 'provider_google')[1]
facebook_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'auth_oauth', 'provider_facebook')[1]
rg = self.pool.get('auth.oauth.provider').read(cr, uid, [google_id], ['enabled','client_id'], context=context)
rf = self.pool.get('auth.oauth.provider').read(cr, uid, [facebook_id], ['enabled','client_id'], context=context)
return {
'auth_oauth_google_enabled': rg[0]['enabled'],
'auth_oauth_google_client_id': rg[0]['client_id'],
'auth_oauth_facebook_enabled': rf[0]['enabled'],
'auth_oauth_facebook_client_id': rf[0]['client_id'],
}
def set_oauth_providers(self, cr, uid, ids, context=None):
google_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'auth_oauth', 'provider_google')[1]
facebook_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'auth_oauth', 'provider_facebook')[1]
config = self.browse(cr, uid, ids[0], context=context)
rg = {
'enabled':config.auth_oauth_google_enabled,
'client_id':config.auth_oauth_google_client_id,
}
rf = {
'enabled':config.auth_oauth_facebook_enabled,
'client_id':config.auth_oauth_facebook_client_id,
}
self.pool.get('auth.oauth.provider').write(cr, uid, [google_id], rg)
self.pool.get('auth.oauth.provider').write(cr, uid, [facebook_id], rf)

View File

@ -0,0 +1,50 @@
<?xml version="1.0"?>
<openerp>
<data>
<record model="ir.ui.view" id="view_general_configuration">
<field name="name">base.config.settings.oauth</field>
<field name="model">base.config.settings</field>
<field name="inherit_id" ref="base_setup.view_general_configuration"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='module_auth_oauth']/.." position="after">
<div attrs="{'invisible':[('module_auth_oauth','=',False)]}">
<div name="google">
<div>
<field name="auth_oauth_google_enabled" class="oe_inline"/>
<label for="auth_oauth_google_enabled"/>
</div>
<div attrs="{'invisible':[('auth_oauth_google_enabled','=',False)]}">
<blockquote>
To setup the signin process with Google, first you have to perform the following steps:<br/>
<br/>
- Go to the <a href="https://code.google.com/apis/console/">Google APIs console</a><br/>
- Ceate a new project<br/>
- Go to Api Access<br/>
- Create an oauth client_id<br/>
- Edit settings and set both Authorized Redirect URIs and Authorized JavaScript Origins to your hostname.<br/>
<br/>
Now copy paste the client_id here: <field name="auth_oauth_google_client_id" class="oe_inline" placeholder="e.g. 1234-xyz.apps.googleusercontent.com"/>
</blockquote>
</div>
</div>
<div name="facebook">
<div>
<field name="auth_oauth_facebook_enabled" class="oe_inline"/>
<label for="auth_oauth_facebook_enabled"/>
</div>
<div attrs="{'invisible':[('auth_oauth_facebook_enabled','=',False)]}">
<blockquote>
To setup the signin process with Google, first you have to perform the following steps:<br/>
<br/>
Now copy paste the client_id here: <field name="auth_oauth_facebook_client_id" class="oe_inline" placeholder="e.g. 1234-xyz.apps.googleusercontent.com"/>
</blockquote>
</div>
</div>
</div>
</xpath>
</field>
</record>
</data>
</openerp>

View File

@ -21,9 +21,7 @@ openerp.auth_oauth = function(instance) {
}, },
on_oauth_loaded: function(result) { on_oauth_loaded: function(result) {
this.oauth_providers = result; this.oauth_providers = result;
console.log(result);
var buttons = QWeb.render("auth_oauth.Login.button",{"widget":this}); var buttons = QWeb.render("auth_oauth.Login.button",{"widget":this});
console.log(buttons);
this.$(".oe_login_pane form ul").after(buttons); this.$(".oe_login_pane form ul").after(buttons);
}, },
on_oauth_sign_in: function(ev) { on_oauth_sign_in: function(ev) {

View File

@ -25,7 +25,8 @@ class base_config_settings(osv.TransientModel):
_inherit = 'base.config.settings' _inherit = 'base.config.settings'
_columns = { _columns = {
'auth_signup_template_user_id': fields.many2one('res.users', 'Template user for new users created through signup') 'auth_signup_uninvited': fields.boolean('allow public users to sign up', help="If unchecked only invited users may sign up"),
'auth_signup_template_user_id': fields.many2one('res.users', 'Template user for new users created through signup'),
} }
def get_default_signup(self, cr, uid, fields, context=None): def get_default_signup(self, cr, uid, fields, context=None):

View File

@ -7,16 +7,15 @@
<field name="model">base.config.settings</field> <field name="model">base.config.settings</field>
<field name="inherit_id" ref="base_setup.view_general_configuration"/> <field name="inherit_id" ref="base_setup.view_general_configuration"/>
<field name="arch" type="xml"> <field name="arch" type="xml">
<xpath expr="//group[last()]" position="after"> <xpath expr="//field[@name='module_auth_anonymous']/.." position="after">
<group> <div>
<label for="id" string="External Users"/> <field name="auth_signup_uninvited" class="oe_inline"/>
<div> <label for="auth_signup_uninvited"/>
<div> </div>
<label for="auth_signup_template_user_id"/> <div>
<field name="auth_signup_template_user_id" class="oe_inline"/> <label for="auth_signup_template_user_id"/>
</div> <field name="auth_signup_template_user_id" class="oe_inline"/>
</div> </div>
</group>
</xpath> </xpath>
</field> </field>
</record> </record>

View File

@ -16,6 +16,14 @@
<field name="name">Off-site Meeting</field> <field name="name">Off-site Meeting</field>
</record> </record>
<record model="crm.meeting.type" id="categ_meet4">
<field name="name">Open Discussion</field>
</record>
<record model="crm.meeting.type" id="categ_meet5">
<field name="name">Feedback Meeting</field>
</record>
<record model="res.request.link" id="request_link_meeting"> <record model="res.request.link" id="request_link_meeting">
<field name="name">Meeting</field> <field name="name">Meeting</field>
<field name="object">crm.meeting</field> <field name="object">crm.meeting</field>

View File

@ -27,7 +27,7 @@
<field eval="time.strftime('%Y-%m-05 12:00:00')" name="date"/> <field eval="time.strftime('%Y-%m-05 12:00:00')" name="date"/>
<field eval="time.strftime('%Y-%m-05 19:00:00')" name="date_deadline"/> <field eval="time.strftime('%Y-%m-05 19:00:00')" name="date_deadline"/>
<field eval="7.0" name="duration"/> <field eval="7.0" name="duration"/>
<field name="state">draft</field> <field name="state">open</field>
</record> </record>
<record id="crm_meeting_3" model="crm.meeting"> <record id="crm_meeting_3" model="crm.meeting">
@ -39,7 +39,7 @@
<field eval="time.strftime('%Y-%m-12 15:55:05')" name="date"/> <field eval="time.strftime('%Y-%m-12 15:55:05')" name="date"/>
<field eval="time.strftime('%Y-%m-12 18:55:05')" name="date_deadline"/> <field eval="time.strftime('%Y-%m-12 18:55:05')" name="date_deadline"/>
<field eval="3.0" name="duration"/> <field eval="3.0" name="duration"/>
<field name="state">done</field> <field name="state">open</field>
</record> </record>
<record id="crm_meeting_4" model="crm.meeting"> <record id="crm_meeting_4" model="crm.meeting">
@ -61,7 +61,7 @@
<field eval="time.strftime('%Y-%m-22 11:05:00')" name="date"/> <field eval="time.strftime('%Y-%m-22 11:05:00')" name="date"/>
<field eval="time.strftime('%Y-%m-22 16:05:00')" name="date_deadline"/> <field eval="time.strftime('%Y-%m-22 16:05:00')" name="date_deadline"/>
<field eval="5" name="duration"/> <field eval="5" name="duration"/>
<field name="state">draft</field> <field name="state">open</field>
</record> </record>
<record id="crm_meeting_6" model="crm.meeting"> <record id="crm_meeting_6" model="crm.meeting">
@ -72,7 +72,7 @@
<field eval="time.strftime('%Y-%m-18 2:00:00')" name="date"/> <field eval="time.strftime('%Y-%m-18 2:00:00')" name="date"/>
<field eval="time.strftime('%Y-%m-18 10:30:00')" name="date_deadline"/> <field eval="time.strftime('%Y-%m-18 10:30:00')" name="date_deadline"/>
<field eval="8.5" name="duration"/> <field eval="8.5" name="duration"/>
<field name="state">done</field> <field name="state">open</field>
</record> </record>
</data> </data>
</openerp> </openerp>

View File

@ -70,7 +70,7 @@
<field name="arch" type="xml"> <field name="arch" type="xml">
<form string="Meetings" version="7.0"> <form string="Meetings" version="7.0">
<header> <header>
<field name="state"/> <field name="state" invisible="True"/>
</header> </header>
<sheet> <sheet>
<div class="oe_title"> <div class="oe_title">
@ -115,7 +115,7 @@
<label for="allday" string="All Day?"/>) <label for="allday" string="All Day?"/>)
</div> </div>
<field name="recurrency" <field name="recurrency"
attrs="{'readonly': ['|', ('recurrent_uid','!=',False), ('state','=','done')]}"/> attrs="{'readonly': [('recurrent_uid','!=',False)]}"/>
</group> </group>
<group> <group>
<field name="alarm_id" widget="selection" /> <field name="alarm_id" widget="selection" />
@ -171,7 +171,6 @@
<button string="Invite People" <button string="Invite People"
name="%(base_calendar.action_view_calendar_invite_attendee_wizard)d" name="%(base_calendar.action_view_calendar_invite_attendee_wizard)d"
icon="terp-partner" type="action" icon="terp-partner" type="action"
attrs="{'readonly': [('state', '=', 'done')]}"
context="{'model' : 'crm.meeting', 'attendee_field':'attendee_ids'}" colspan="2"/> context="{'model' : 'crm.meeting', 'attendee_field':'attendee_ids'}" colspan="2"/>
<field name="attendee_ids" widget="one2many" mode="tree"> <field name="attendee_ids" widget="one2many" mode="tree">
<tree string="Invitation details" editable="top"> <tree string="Invitation details" editable="top">
@ -245,12 +244,11 @@
<field name="name">CRM - Meetings Tree</field> <field name="name">CRM - Meetings Tree</field>
<field name="model">crm.meeting</field> <field name="model">crm.meeting</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<tree string="Meetings" fonts="bold:needaction_pending==True" <tree string="Meetings" fonts="bold:needaction_pending==True">
colors="red:state=='open';black:state in ('draft', 'cancel','done','pending')">
<field name="name" string="Subject" /> <field name="name" string="Subject" />
<field name="user_id"/> <field name="user_id"/>
<field name="date"/> <field name="date"/>
<field name="state"/> <field name="state" invisible="True"/>
<field name="duration" /> <field name="duration" />
<field name="needaction_pending" invisible="1"/> <field name="needaction_pending" invisible="1"/>
</tree> </tree>

View File

@ -28,10 +28,13 @@ class base_config_settings(osv.osv_memory):
'module_multi_company': fields.boolean('manage multiple companies', 'module_multi_company': fields.boolean('manage multiple companies',
help="""Work in multi-company environments, with appropriate security access between companies. help="""Work in multi-company environments, with appropriate security access between companies.
This installs the module multi_company."""), This installs the module multi_company."""),
'module_portal': fields.boolean('activate customer portal',
help="""The portal will give access to a series of documents for your customers; his quotations, his invoices, his projects, etc."""),
'module_share': fields.boolean('allow documents sharing', 'module_share': fields.boolean('allow documents sharing',
help="""As an example, you will be able to share a project or some tasks to your customers, or quotes/sales to several persons at your customer company, or your agenda availabilities to your contacts."""), help="""Share or embbed any screen of openerp."""),
'module_portal': fields.boolean('activate the customer/supplier portal',
help="""Give access your customers and suppliers to their documents."""),
'module_auth_anonymous': fields.boolean('activate the public portal',
help="""Enable the public part of openerp, openerp becomes a public website."""),
'module_auth_oauth': fields.boolean('use external authentication providers, sign in with google, facebook, ...'),
} }
def open_company(self, cr, uid, ids, context=None): def open_company(self, cr, uid, ids, context=None):

View File

@ -28,20 +28,34 @@
</div> </div>
</group> </group>
<group> <group>
<label for="id" string="Share Data"/> <label for="id" string="Email"/>
<div> <div name="email">
<div> <div>
<field name="module_share" class="oe_inline"/> <button type="action"
<label for="module_share"/> name="%(base.action_ir_mail_server_list)d"
string="Configure outgoing email servers" class="oe_link"/>
</div> </div>
</div>
</group>
<group>
<label for="id" string="Portal access"/>
<div>
<div> <div>
<field name="module_portal" class="oe_inline"/> <field name="module_portal" class="oe_inline"/>
<label for="module_portal"/> <label for="module_portal"/>
</div> </div>
<div> <div>
<button type="action" <field name="module_auth_anonymous" class="oe_inline"/>
name="%(base.action_ir_mail_server_list)d" <label for="module_auth_anonymous"/>
string="Configure outgoing email servers" class="oe_link"/> </div>
</div>
</group>
<group>
<label for="id" string="Authentication"/>
<div>
<div>
<field name="module_auth_oauth" class="oe_inline"/>
<label for="module_auth_oauth"/>
</div> </div>
</div> </div>
</group> </group>

View File

@ -49,7 +49,6 @@ Can you send details,</field>
<field name="section_id" ref="crm_case_section_3"/> <field name="section_id" ref="crm_case_section_3"/>
<field name="user_id" ref="base.user_admin"/> <field name="user_id" ref="base.user_admin"/>
<field name="stage_id" ref="stage_lead1"/> <field name="stage_id" ref="stage_lead1"/>
<field name="description">Want to know features and benifits to use the new software.</field>
<field eval="1" name="active"/> <field eval="1" name="active"/>
</record> </record>
@ -550,53 +549,86 @@ Andrew</field>
<!-- Some messages linked to the previous opportunity --> <!-- Some messages linked to the previous opportunity -->
<record id="message_email0" model="mail.message"> <record id="message_email0" model="mail.message">
<field name="subject">Kitchen design</field> <field name="subject">Plan to buy a Laptop</field>
<field name="model">crm.lead</field> <field name="model">crm.lead</field>
<field name="res_id" ref="crm_case_15"/> <field name="res_id" ref="crm_case_15"/>
<field name="content_subtype">html</field> <field name="content_subtype">html</field>
<field name="body_html"><![CDATA[Email0 inquiry]]></field> <field name="body_html">&lt;![CDATA[Email0 inquiry]]&gt;&lt;div&gt;&lt;font size="2"&gt;Hello,&lt;/font&gt;&lt;/div&gt;&lt;div&gt;&lt;font size="2"&gt;&lt;br&gt;&lt;/font&gt;&lt;/div&gt;&lt;div&gt;&lt;font size="2"&gt;I am interested in your company's product and I plan to buy a new laptop having latest technologies and affordable price.&lt;/font&gt;&lt;/div&gt;&lt;div&gt;&lt;font size="2"&gt;Can you please send me product catalogue?&lt;/font&gt;&lt;/div&gt;</field>
<field name="type">email</field>
<field name="state">sent</field>
<field name="user_id" ref="base.user_root"></field>
</record>
<record id="message_note0" model="mail.message">
<field name="subject">Reply</field>
<field name="model">crm.lead</field>
<field name="res_id" ref="crm_case_15"/>
<field name="content_subtype">html</field>
<field name="body_html"><![CDATA[Internal note0]]></field>
<field name="type">comment</field>
<field name="user_id" ref="base.user_root"></field>
</record>
<record id="message_note0_comment0" model="mail.message">
<field name="subject">Reply</field>
<field name="model">crm.lead</field>
<field name="res_id" ref="crm_case_15"/>
<field name="content_subtype">html</field>
<field name="body_html"><![CDATA[comment on note0]]></field>
<field name="parent_id" ref="message_note0"/>
<field name="type">comment</field>
<field name="user_id" ref="base.user_root"></field>
</record>
<record id="message_note0_comment1" model="mail.message">
<field name="subject">Reply</field>
<field name="model">crm.lead</field>
<field name="res_id" ref="crm_case_15"/>
<field name="content_subtype">html</field>
<field name="body_html"><![CDATA[comment1 on note0]]></field>
<field name="parent_id" ref="message_note0"/>
<field name="type">comment</field>
<field name="user_id" ref="base.user_root"></field>
</record>
<record id="message_email1" model="mail.message">
<field name="subject">Your inquiry</field>
<field name="model">crm.lead</field>
<field name="res_id" ref="crm_case_15"/>
<field name="content_subtype">html</field>
<field name="body_html"><![CDATA[hello, outgoing email]]></field>
<field name="type">email</field> <field name="type">email</field>
<field name="state">received</field> <field name="state">received</field>
<field name="user_id" ref="base.user_root"></field> <field name="user_id" ref="base.user_demo"/>
</record>
<record id="message_note0" model="mail.message">
<field name="subject">Re: Plan to buy a Laptop</field>
<field name="model">crm.lead</field>
<field name="res_id" ref="crm_case_15"/>
<field name="parent_id" ref="message_email0"/>
<field name="content_subtype">plain</field>
<field name="body_text">Dear Customer,
Thanks for showing interest in our products.
We have attached the catalogue,
We would like to know your interests, Let us know if we can call you for more details.
Thanks</field>
<field name="type">email</field>
<field name="user_id" ref="base.user_root"/>
<field name="state">sent</field>
</record>
<record id="message_note0_comment0" model="mail.message">
<field name="subject">Re: Plan to buy a Laptop</field>
<field name="model">crm.lead</field>
<field name="res_id" ref="crm_case_15"/>
<field name="content_subtype">html</field>
<field name="body_html">&lt;div&gt;Thanks for the information,&lt;/div&gt;&lt;div&gt;I will visit the store soon.&lt;/div&gt;</field>
<field name="parent_id" ref="message_note0"/>
<field name="type">email</field>
<field name="user_id" ref="base.user_demo"/>
<field name="state">received</field>
</record>
<record id="message_note0_comment1" model="mail.message">
<field name="subject">Re: Plan to buy a Laptop</field>
<field name="model">crm.lead</field>
<field name="res_id" ref="crm_case_15"/>
<field name="content_subtype">html</field>
<field name="body_html">&lt;font color="#1f1f1f"&gt;Can you tell me if the store is open at 9:00 PM?&lt;/b&gt;&lt;/font&gt;</field>
<field name="parent_id" ref="message_note0"/>
<field name="type">email</field>
<field name="state">received</field>
<field name="user_id" ref="base.user_demo"/>
</record>
<record id="message_email1" model="mail.message">
<field name="subject">Re: Plan to buy a Laptop</field>
<field name="model">crm.lead</field>
<field name="res_id" ref="crm_case_15"/>
<field name="content_subtype">plain</field>
<field name="body_text">Yes, its open till 10:00 PM, you are welcome!</field>
<field name="type">email</field>
<field name="state">sent</field>
<field name="user_id" ref="base.user_root"/>
</record>
<record id="message_email_12" model="mail.message">
<field name="subject">Inquiry</field>
<field name="model">crm.lead</field>
<field name="res_id" ref="crm_case_1"/>
<field name="content_subtype">plain</field>
<field name="body_text">Hello,
I am Jason from Le Club SARL,
I am intertested to attend Training organized in your company,
Can you send details,</field>
<field name="type">email</field>
<field name="state">received</field>
<field name="user_id" ref="base.user_demo"/>
</record>
<record id="message_email_13" model="mail.message">
<field name="subject">Need Details</field>
<field name="model">crm.lead</field>
<field name="res_id" ref="crm_case_2"/>
<field name="content_subtype">plain</field>
<field name="body_text">Want to know features and benifits to use the new software.</field>
<field name="type">comment</field>
<field name="user_id" ref="base.user_demo"/>
</record> </record>

View File

@ -2,9 +2,8 @@
<openerp> <openerp>
<data> <data>
<!-- Calendar Attendee Form View --> <!-- Calendar Attendee Form View -->
<record id="attendee_form_view_inherit" model="ir.ui.view">
<record id="attendee_form_view_inherit" model="ir.ui.view">
<field name="name">calendar.attendee.form.inherit</field> <field name="name">calendar.attendee.form.inherit</field>
<field name="model">calendar.attendee</field> <field name="model">calendar.attendee</field>
<field name="inherit_id" ref="base_calendar.base_calendar_attendee_form_view"/> <field name="inherit_id" ref="base_calendar.base_calendar_attendee_form_view"/>
@ -13,7 +12,7 @@
<field name="categ_id" string="Event Type"/> <field name="categ_id" string="Event Type"/>
</field> </field>
</field> </field>
</record> </record>
</data> </data>
</openerp> </openerp>

File diff suppressed because it is too large Load Diff

View File

@ -35,8 +35,7 @@ The whole workflow is implemented:
* Draft expense * Draft expense
* Confirmation of the sheet by the employee * Confirmation of the sheet by the employee
* Validation by his manager * Validation by his manager
* Validation by the accountant and invoice creation * Validation by the accountant and receipt creation
* Payment of the invoice to the employee
This module also uses the analytic accounting and is compatible with This module also uses the analytic accounting and is compatible with
the invoice on timesheet module so that you will be able to automatically the invoice on timesheet module so that you will be able to automatically
@ -45,7 +44,7 @@ re-invoice your customer's expenses if your work by project.
'author': 'OpenERP SA', 'author': 'OpenERP SA',
'website': 'http://www.openerp.com', 'website': 'http://www.openerp.com',
'images': ['images/hr_expenses_analysis.jpeg', 'images/hr_expenses.jpeg'], 'images': ['images/hr_expenses_analysis.jpeg', 'images/hr_expenses.jpeg'],
'depends': ['hr', 'account'], 'depends': ['hr', 'account_voucher'],
'data': [ 'data': [
'security/ir.model.access.csv', 'security/ir.model.access.csv',
'hr_expense_data.xml', 'hr_expense_data.xml',

View File

@ -40,12 +40,16 @@ class hr_expense_expense(osv.osv):
if context is None: if context is None:
context = {} context = {}
if not default: default = {} if not default: default = {}
default.update({'invoice_id': False, 'date_confirm': False, 'date_valid': False, 'user_valid': False}) default.update({'voucher_id': False, 'date_confirm': False, 'date_valid': False, 'user_valid': False})
return super(hr_expense_expense, self).copy(cr, uid, id, default, context=context) return super(hr_expense_expense, self).copy(cr, uid, id, default, context=context)
def _amount(self, cr, uid, ids, field_name, arg, context=None): def _amount(self, cr, uid, ids, field_name, arg, context=None):
cr.execute("SELECT s.id,COALESCE(SUM(l.unit_amount*l.unit_quantity),0) AS amount FROM hr_expense_expense s LEFT OUTER JOIN hr_expense_line l ON (s.id=l.expense_id) WHERE s.id IN %s GROUP BY s.id ", (tuple(ids),)) res= {}
res = dict(cr.fetchall()) for expense in self.browse(cr, uid, ids, context=context):
total = 0.0
for line in expense.line_ids:
total += line.unit_amount * line.unit_quantity
res[expense.id] = total
return res return res
def _get_currency(self, cr, uid, context=None): def _get_currency(self, cr, uid, context=None):
@ -63,7 +67,7 @@ class hr_expense_expense(osv.osv):
'name': fields.char('Description', size=128, required=True), 'name': fields.char('Description', size=128, required=True),
'id': fields.integer('Sheet ID', readonly=True), 'id': fields.integer('Sheet ID', readonly=True),
'date': fields.date('Date', select=True), 'date': fields.date('Date', select=True),
'journal_id': fields.many2one('account.journal', 'Force Journal', help = "The journal used when the expense is invoiced"), 'journal_id': fields.many2one('account.journal', 'Force Journal', help = "The journal used when the expense is done."),
'employee_id': fields.many2one('hr.employee', "Employee", required=True), 'employee_id': fields.many2one('hr.employee', "Employee", required=True),
'user_id': fields.many2one('res.users', 'User', required=True), 'user_id': fields.many2one('res.users', 'User', required=True),
'date_confirm': fields.date('Confirmation Date', select=True, help = "Date of the confirmation of the sheet expense. It's filled when the button Confirm is pressed."), 'date_confirm': fields.date('Confirmation Date', select=True, help = "Date of the confirmation of the sheet expense. It's filled when the button Confirm is pressed."),
@ -73,7 +77,7 @@ class hr_expense_expense(osv.osv):
'line_ids': fields.one2many('hr.expense.line', 'expense_id', 'Expense Lines', readonly=True, states={'draft':[('readonly',False)]} ), 'line_ids': fields.one2many('hr.expense.line', 'expense_id', 'Expense Lines', readonly=True, states={'draft':[('readonly',False)]} ),
'note': fields.text('Note'), 'note': fields.text('Note'),
'amount': fields.function(_amount, string='Total Amount', digits_compute= dp.get_precision('Account')), 'amount': fields.function(_amount, string='Total Amount', digits_compute= dp.get_precision('Account')),
'invoice_id': fields.many2one('account.invoice', "Employee's Invoice"), 'voucher_id': fields.many2one('account.voucher', "Employee's Receipt"),
'currency_id': fields.many2one('res.currency', 'Currency', required=True), 'currency_id': fields.many2one('res.currency', 'Currency', required=True),
'department_id':fields.many2one('hr.department','Department'), 'department_id':fields.many2one('hr.department','Department'),
'company_id': fields.many2one('res.company', 'Company', required=True), 'company_id': fields.many2one('res.company', 'Company', required=True),
@ -82,11 +86,10 @@ class hr_expense_expense(osv.osv):
('cancelled', 'Refused'), ('cancelled', 'Refused'),
('confirm', 'Waiting Approval'), ('confirm', 'Waiting Approval'),
('accepted', 'Approved'), ('accepted', 'Approved'),
('invoiced', 'Invoiced'), ('done', 'Done'),
('paid', 'Reimbursed')
], ],
'Status', readonly=True, help='When the expense request is created the status is \'Draft\'.\n It is confirmed by the user and request is sent to admin, the status is \'Waiting Confirmation\'.\ 'Status', readonly=True, help='When the expense request is created the status is \'Draft\'.\n It is confirmed by the user and request is sent to admin, the status is \'Waiting Confirmation\'.\
\nIf the admin accepts it, the status is \'Accepted\'.\n If an invoice is made for the expense request, the status is \'Invoiced\'.\n If the expense is paid to user, the status is \'Reimbursed\'.'), \nIf the admin accepts it, the status is \'Accepted\'.\n If a receipt is made for the expense request, the status is \'Done\'.'),
} }
_defaults = { _defaults = {
'company_id': lambda s, cr, uid, c: s.pool.get('res.company')._company_default_get(cr, uid, 'hr.employee', context=c), 'company_id': lambda s, cr, uid, c: s.pool.get('res.company')._company_default_get(cr, uid, 'hr.employee', context=c),
@ -97,6 +100,13 @@ class hr_expense_expense(osv.osv):
'currency_id': _get_currency, 'currency_id': _get_currency,
} }
def onchange_currency_id(self, cr, uid, ids, currency_id=False, company_id=False, context=None):
res = {'value': {'journal_id': False}}
journal_ids = self.pool.get('account.journal').search(cr, uid, [('type','=','purchase'), ('currency','=',currency_id), ('company_id', '=', company_id)], context=context)
if journal_ids:
res['value']['journal_id'] = journal_ids[0]
return res
def onchange_employee_id(self, cr, uid, ids, employee_id, context=None): def onchange_employee_id(self, cr, uid, ids, employee_id, context=None):
emp_obj = self.pool.get('hr.employee') emp_obj = self.pool.get('hr.employee')
department_id = False department_id = False
@ -126,101 +136,94 @@ class hr_expense_expense(osv.osv):
self.write(cr, uid, ids, {'state':'cancelled'}) self.write(cr, uid, ids, {'state':'cancelled'})
return True return True
def expense_paid(self, cr, uid, ids, *args): def action_receipt_create(self, cr, uid, ids, context=None):
self.write(cr, uid, ids, {'state':'paid'})
return True
def invoice(self, cr, uid, ids, context=None):
wf_service = netsvc.LocalService("workflow")
mod_obj = self.pool.get('ir.model.data')
res = mod_obj.get_object_reference(cr, uid, 'account', 'invoice_supplier_form')
inv_ids = []
for id in ids:
wf_service.trg_validate(uid, 'hr.expense.expense', id, 'invoice', cr)
inv_ids.append(self.browse(cr, uid, id).invoice_id.id)
return {
'name': _('Supplier Invoices'),
'view_type': 'form',
'view_mode': 'form',
'view_id': [res and res[1] or False],
'res_model': 'account.invoice',
'context': "{'type':'out_invoice', 'journal_type': 'purchase'}",
'type': 'ir.actions.act_window',
'nodestroy': True,
'target': 'current',
'res_id': inv_ids and inv_ids[0] or False,
}
def action_invoice_create(self, cr, uid, ids):
res = False
invoice_obj = self.pool.get('account.invoice')
property_obj = self.pool.get('ir.property') property_obj = self.pool.get('ir.property')
sequence_obj = self.pool.get('ir.sequence') sequence_obj = self.pool.get('ir.sequence')
analytic_journal_obj = self.pool.get('account.analytic.journal') analytic_journal_obj = self.pool.get('account.analytic.journal')
account_journal = self.pool.get('account.journal') account_journal = self.pool.get('account.journal')
for exp in self.browse(cr, uid, ids): voucher_obj = self.pool.get('account.voucher')
currency_obj = self.pool.get('res.currency')
wkf_service = netsvc.LocalService("workflow")
if context is None:
context = {}
for exp in self.browse(cr, uid, ids, context=context):
company_id = exp.company_id.id company_id = exp.company_id.id
lines = [] lines = []
for l in exp.line_ids: total = 0.0
tax_id = [] ctx = context.copy()
if l.product_id: ctx.update({'date': exp.date})
acc = l.product_id.product_tmpl_id.property_account_expense journal = False
if exp.journal_id:
journal = exp.journal_id
else:
journal_id = voucher_obj._get_journal(cr, uid, context={'type': 'purchase', 'company_id': company_id})
if journal_id:
journal = account_journal.browse(cr, uid, journal_id, context=context)
for line in exp.line_ids:
if line.product_id:
acc = line.product_id.product_tmpl_id.property_account_expense
if not acc: if not acc:
acc = l.product_id.categ_id.property_account_expense_categ acc = line.product_id.categ_id.property_account_expense_categ
tax_id = [x.id for x in l.product_id.supplier_taxes_id]
else: else:
acc = property_obj.get(cr, uid, 'property_account_expense_categ', 'product.category', context={'force_company': company_id}) acc = property_obj.get(cr, uid, 'property_account_expense_categ', 'product.category', context={'force_company': company_id})
if not acc: if not acc:
raise osv.except_osv(_('Error!'), _('Please configure Default Expense account for Product purchase: `property_account_expense_categ`.')) raise osv.except_osv(_('Error!'), _('Please configure Default Expense account for Product purchase: `property_account_expense_categ`.'))
total_amount = line.total_amount
if journal.currency:
if exp.currency_id != journal.currency:
total_amount = currency_obj.compute(cr, uid, exp.currency_id.id, journal.currency.id, total_amount, context=ctx)
elif exp.currency_id != exp.company_id.currency_id:
total_amount = currency_obj.compute(cr, uid, exp.currency_id.id, exp.company_id.currency_id.id, total_amount, context=ctx)
lines.append((0, False, { lines.append((0, False, {
'name': l.name, 'name': line.name,
'account_id': acc.id, 'account_id': acc.id,
'price_unit': l.unit_amount, 'account_analytic_id': line.analytic_account.id,
'quantity': l.unit_quantity, 'amount': total_amount,
'uos_id': l.uom_id.id, 'type': 'dr'
'product_id': l.product_id and l.product_id.id or False,
'invoice_line_tax_id': tax_id and [(6, 0, tax_id)] or False,
'account_analytic_id': l.analytic_account.id,
})) }))
total += total_amount
if not exp.employee_id.address_home_id: if not exp.employee_id.address_home_id:
raise osv.except_osv(_('Error!'), _('The employee must have a home address.')) raise osv.except_osv(_('Error!'), _('The employee must have a home address.'))
acc = exp.employee_id.address_home_id.property_account_payable.id acc = exp.employee_id.address_home_id.property_account_payable.id
payment_term_id = exp.employee_id.address_home_id.property_payment_term.id voucher = {
inv = {
'name': exp.name, 'name': exp.name,
'reference': sequence_obj.get(cr, uid, 'hr.expense.invoice'), 'reference': sequence_obj.get(cr, uid, 'hr.expense.invoice'),
'account_id': acc, 'account_id': acc,
'type': 'in_invoice', 'type': 'purchase',
'partner_id': exp.employee_id.address_home_id.id, 'partner_id': exp.employee_id.address_home_id.id,
'company_id': company_id, 'company_id': company_id,
'origin': exp.name, 'line_ids': lines,
'invoice_line': lines, 'amount': total,
'currency_id': exp.currency_id.id, 'journal_id': journal.id,
'payment_term': payment_term_id,
'fiscal_position': exp.employee_id.address_home_id.property_account_position.id
} }
if payment_term_id:
to_update = invoice_obj.onchange_payment_term_date_invoice(cr, uid, [], payment_term_id, None)
if to_update:
inv.update(to_update['value'])
journal = False
if exp.journal_id:
inv['journal_id']=exp.journal_id.id
journal = exp.journal_id
else:
journal_id = invoice_obj._get_journal(cr, uid, context={'type': 'in_invoice', 'company_id': company_id})
if journal_id:
inv['journal_id'] = journal_id
journal = account_journal.browse(cr, uid, journal_id)
if journal and not journal.analytic_journal_id: if journal and not journal.analytic_journal_id:
analytic_journal_ids = analytic_journal_obj.search(cr, uid, [('type','=','purchase')]) analytic_journal_ids = analytic_journal_obj.search(cr, uid, [('type','=','purchase')], context=context)
if analytic_journal_ids: if analytic_journal_ids:
account_journal.write(cr, uid, [journal.id],{'analytic_journal_id':analytic_journal_ids[0]}) account_journal.write(cr, uid, [journal.id], {'analytic_journal_id': analytic_journal_ids[0]}, context=context)
inv_id = invoice_obj.create(cr, uid, inv, {'type': 'in_invoice'}) voucher_id = voucher_obj.create(cr, uid, voucher, context=context)
invoice_obj.button_compute(cr, uid, [inv_id], {'type': 'in_invoice'}, set_total=True) wkf_service.trg_validate(uid, 'account.voucher', voucher_id, 'proforma_voucher', cr)
self.write(cr, uid, [exp.id], {'invoice_id': inv_id, 'state': 'invoiced'}) self.write(cr, uid, [exp.id], {'voucher_id': voucher_id, 'state': 'done'}, context=context)
res = inv_id return True
return res
def action_view_receipt(self, cr, uid, ids, context=None):
'''
This function returns an action that display existing receipt of given expense ids.
'''
assert len(ids) == 1, 'This option should only be used for a single id at a time'
voucher_id = self.browse(cr, uid, ids[0], context=context).voucher_id.id
res = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'account_voucher', 'view_purchase_receipt_form')
result = {
'name': _('Expense Receipt'),
'view_type': 'form',
'view_mode': 'form',
'view_id': res and res[1] or False,
'res_model': 'account.voucher',
'type': 'ir.actions.act_window',
'nodestroy': True,
'target': 'current',
'res_id': voucher_id,
}
return result
hr_expense_expense() hr_expense_expense()

View File

@ -31,7 +31,7 @@
<field name="date"/> <field name="date"/>
<field name="user_id" invisible="1"/> <field name="user_id" invisible="1"/>
<field name="name"/> <field name="name"/>
<field name="currency_id"/> <field name="currency_id" groups="base.group_multi_currency"/>
<field name="amount"/> <field name="amount"/>
<field name="state"/> <field name="state"/>
</tree> </tree>
@ -42,7 +42,7 @@
<field name="name">hr.expense.expense.tree</field> <field name="name">hr.expense.expense.tree</field>
<field name="model">hr.expense.expense</field> <field name="model">hr.expense.expense</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<tree colors="blue:state == 'draft';black:state in ('confirm','accepted','invoiced','paid');gray:state == 'cancelled'" string="Expenses" editable="top"> <tree colors="blue:state == 'draft';black:state in ('confirm','accepted','done');gray:state == 'cancelled'" string="Expenses" editable="top">
<field name="employee_id"/> <field name="employee_id"/>
<field name="date"/> <field name="date"/>
<field name="department_id"/> <field name="department_id"/>
@ -64,9 +64,10 @@
<button name="confirm" states="draft" string="Submit to Manager" type="workflow" class="oe_highlight"/> <button name="confirm" states="draft" string="Submit to Manager" type="workflow" class="oe_highlight"/>
<button name="validate" states="confirm" string="Approve" type="workflow" groups="base.group_hr_user" class="oe_highlight"/> <button name="validate" states="confirm" string="Approve" type="workflow" groups="base.group_hr_user" class="oe_highlight"/>
<button name="draft" states="confirm,cancelled" string="Set to Draft" type="workflow" groups="base.group_hr_user" /> <button name="draft" states="confirm,cancelled" string="Set to Draft" type="workflow" groups="base.group_hr_user" />
<button name="invoice" states="accepted" string="Invoice" type="object" groups="base.group_hr_user" class="oe_highlight"/> <button name="done" states="accepted" string="Generate Accounting Entries" type="workflow" groups="account.group_account_invoice" class="oe_highlight"/>
<button name="action_view_receipt" states="done" string="Open Receipt" type="object"/>
<button name="refuse" states="confirm,accepted" string="Refuse" type="workflow" groups="base.group_hr_user" /> <button name="refuse" states="confirm,accepted" string="Refuse" type="workflow" groups="base.group_hr_user" />
<field name="state" widget="statusbar" statusbar_visible="draft,confirm,accepted" statusbar_colors='{"confirm":"blue","cancelled":"red"}'/> <field name="state" widget="statusbar" statusbar_visible="draft,confirm,accepted,done" statusbar_colors='{"confirm":"blue","cancelled":"red"}'/>
</header> </header>
<sheet> <sheet>
<group> <group>
@ -79,7 +80,7 @@
<group> <group>
<field name="name"/> <field name="name"/>
<field name="user_valid"/> <field name="user_valid"/>
<field name="currency_id"/> <field name="currency_id" groups="base.group_multi_currency" on_change="onchange_currency_id(currency_id, company_id)"/>
</group> </group>
</group> </group>
<notebook> <notebook>
@ -105,14 +106,21 @@
</group> </group>
</form> </form>
</field> </field>
<separator string="Notes"/> <group>
<field name="note" placeholder="Free Notes"/> <div>
<separator string="Notes"/>
<field name="note" placeholder="Free Notes"/>
</div>
<group class="oe_subtotal_footer">
<field name="amount"/>
</group>
</group>
</page> </page>
<page string="Other Info"> <page string="Other Info">
<group> <group>
<group string="Accounting Data"> <group string="Accounting Data">
<field name="journal_id"/> <field name="journal_id" widget="selection" domain="[('type', '=', 'purchase')]"/>
<field name="invoice_id" context="{'type':'in_invoice', 'journal_type': 'purchase'}"/> <field name="voucher_id" context="{'form_view_ref': 'account_voucher.view_purchase_receipt_form'}"/>
</group> </group>
</group> </group>
</page> </page>

View File

@ -32,14 +32,6 @@
<field name="action">expense_accept()</field> <field name="action">expense_accept()</field>
</record> </record>
<record id="act_paid" model="workflow.activity">
<field name="wkf_id" ref="wkf_expenses"/>
<field name="name">paid</field>
<field name="kind">function</field>
<field name="action">expense_paid()</field>
<field name="flow_stop">True</field>
</record>
<record id="act_refused" model="workflow.activity"> <record id="act_refused" model="workflow.activity">
<field name="wkf_id" ref="wkf_expenses"/> <field name="wkf_id" ref="wkf_expenses"/>
<field name="name">refused</field> <field name="name">refused</field>
@ -47,12 +39,11 @@
<field name="action">expense_canceled()</field> <field name="action">expense_canceled()</field>
</record> </record>
<record id="act_invoice" model="workflow.activity"> <record id="act_done" model="workflow.activity">
<field name="wkf_id" ref="wkf_expenses"/> <field name="wkf_id" ref="wkf_expenses"/>
<field name="name">invoice</field> <field name="name">done</field>
<field name="kind">subflow</field> <field name="kind">function</field>
<field name="subflow_id" ref="account.wkf"/> <field name="action">action_receipt_create()</field>
<field name="action">action_invoice_create()</field>
</record> </record>
<record id="t1" model="workflow.transition"> <record id="t1" model="workflow.transition">
@ -91,15 +82,8 @@
<record id="t8" model="workflow.transition"> <record id="t8" model="workflow.transition">
<field name="act_from" ref="act_accepted"/> <field name="act_from" ref="act_accepted"/>
<field name="act_to" ref="act_invoice"/> <field name="act_to" ref="act_done"/>
<field name="signal">invoice</field> <field name="signal">done</field>
<field name="group_id" ref="base.group_hr_user"/>
</record>
<record id="t9" model="workflow.transition">
<field name="act_from" ref="act_invoice"/>
<field name="act_to" ref="act_paid"/>
<field name="signal">subflow.paid</field>
<field name="group_id" ref="base.group_hr_user"/> <field name="group_id" ref="base.group_hr_user"/>
</record> </record>

View File

@ -39,11 +39,10 @@ class hr_expense_report(osv.osv):
'product_id':fields.many2one('product.product', 'Product', readonly=True), 'product_id':fields.many2one('product.product', 'Product', readonly=True),
'journal_id': fields.many2one('account.journal', 'Force Journal', readonly=True), 'journal_id': fields.many2one('account.journal', 'Force Journal', readonly=True),
'product_qty':fields.float('Qty', readonly=True), 'product_qty':fields.float('Qty', readonly=True),
'invoiced':fields.integer('# of Invoiced Lines', readonly=True),
'employee_id': fields.many2one('hr.employee', "Employee's Name", readonly=True), 'employee_id': fields.many2one('hr.employee', "Employee's Name", readonly=True),
'date_confirm': fields.date('Confirmation Date', readonly=True), 'date_confirm': fields.date('Confirmation Date', readonly=True),
'date_valid': fields.date('Validation Date', readonly=True), 'date_valid': fields.date('Validation Date', readonly=True),
'invoice_id': fields.many2one('account.invoice', 'Invoice', readonly=True), 'voucher_id': fields.many2one('account.voucher', 'Receipt', readonly=True),
'department_id':fields.many2one('hr.department','Department', readonly=True), 'department_id':fields.many2one('hr.department','Department', readonly=True),
'company_id':fields.many2one('res.company', 'Company', readonly=True), 'company_id':fields.many2one('res.company', 'Company', readonly=True),
'user_id':fields.many2one('res.users', 'Validation User', readonly=True), 'user_id':fields.many2one('res.users', 'Validation User', readonly=True),
@ -60,8 +59,7 @@ class hr_expense_report(osv.osv):
('draft', 'Draft'), ('draft', 'Draft'),
('confirm', 'Waiting confirmation'), ('confirm', 'Waiting confirmation'),
('accepted', 'Accepted'), ('accepted', 'Accepted'),
('invoiced', 'Invoiced'), ('done', 'Done'),
('paid', 'Reimbursed'),
('cancelled', 'Cancelled')], ('cancelled', 'Cancelled')],
'Status', readonly=True), 'Status', readonly=True),
} }
@ -78,8 +76,7 @@ class hr_expense_report(osv.osv):
s.currency_id, s.currency_id,
to_date(to_char(s.date_confirm, 'dd-MM-YYYY'),'dd-MM-YYYY') as date_confirm, to_date(to_char(s.date_confirm, 'dd-MM-YYYY'),'dd-MM-YYYY') as date_confirm,
to_date(to_char(s.date_valid, 'dd-MM-YYYY'),'dd-MM-YYYY') as date_valid, to_date(to_char(s.date_valid, 'dd-MM-YYYY'),'dd-MM-YYYY') as date_valid,
s.invoice_id, s.voucher_id,
count(s.invoice_id) as invoiced,
s.user_valid as user_id, s.user_valid as user_id,
s.department_id, s.department_id,
to_char(date_trunc('day',s.create_date), 'YYYY') as year, to_char(date_trunc('day',s.create_date), 'YYYY') as year,
@ -109,7 +106,7 @@ class hr_expense_report(osv.osv):
to_date(to_char(s.date_valid, 'dd-MM-YYYY'),'dd-MM-YYYY'), to_date(to_char(s.date_valid, 'dd-MM-YYYY'),'dd-MM-YYYY'),
l.product_id, l.product_id,
l.analytic_account, l.analytic_account,
s.invoice_id, s.voucher_id,
s.currency_id, s.currency_id,
s.user_valid, s.user_valid,
s.department_id, s.department_id,

View File

@ -6,13 +6,13 @@
<field name="name">hr.expense.report.tree</field> <field name="name">hr.expense.report.tree</field>
<field name="model">hr.expense.report</field> <field name="model">hr.expense.report</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<tree colors="blue:state == 'draft';black:state in ('confirm','accepted','invoiced','paid');gray:state == 'cancelled'" string="Expenses Analysis"> <tree colors="blue:state == 'draft';black:state in ('confirm','accepted','done');gray:state == 'cancelled'" string="Expenses Analysis">
<field name="employee_id" invisible="1"/> <field name="employee_id" invisible="1"/>
<field name="user_id" invisible="1"/> <field name="user_id" invisible="1"/>
<field name="year" invisible="1"/> <field name="year" invisible="1"/>
<field name="month" invisible="1"/> <field name="month" invisible="1"/>
<field name="day" invisible="1"/> <field name="day" invisible="1"/>
<field name="invoice_id" invisible="1"/> <field name="voucher_id" invisible="1"/>
<field name="analytic_account" invisible="1" groups="analytic.group_analytic_accounting"/> <field name="analytic_account" invisible="1" groups="analytic.group_analytic_accounting"/>
<field name="department_id" invisible="1"/> <field name="department_id" invisible="1"/>
<field name="company_id" invisible="1"/> <field name="company_id" invisible="1"/>
@ -22,7 +22,6 @@
<field name="state" invisible="1"/> <field name="state" invisible="1"/>
<field name="nbr" sum="# of Lines"/> <field name="nbr" sum="# of Lines"/>
<field name="no_of_products" sum="# of Products"/> <field name="no_of_products" sum="# of Products"/>
<field name="invoiced" sum="Total Invoiced Lines"/>
<field name="price_average" avg="Average Price"/> <field name="price_average" avg="Average Price"/>
<field name="price_total" sum="Total Price"/> <field name="price_total" sum="Total Price"/>
<field name="delay_confirm"/> <field name="delay_confirm"/>
@ -50,7 +49,7 @@
<search string="Expenses Analysis"> <search string="Expenses Analysis">
<filter string="Waiting" icon="terp-gtk-media-pause" domain="[('state', '=' ,'confirm')]" help = "Confirm Expenses"/> <filter string="Waiting" icon="terp-gtk-media-pause" domain="[('state', '=' ,'confirm')]" help = "Confirm Expenses"/>
<filter string="Approved" icon="terp-check" domain="[('state','=','accepted')]" help = "Approved Expenses"/> <filter string="Approved" icon="terp-check" domain="[('state','=','accepted')]" help = "Approved Expenses"/>
<filter string="Invoiced" icon="terp-dolar" domain="[('state','in', ('invoiced', 'paid'))]" help = "Invoiced Expenses"/> <filter string="Done" icon="terp-dolar" domain="[('state','=', 'done')]" help = "Done Expenses"/>
<field name="employee_id"/> <field name="employee_id"/>
<field name="department_id"/> <field name="department_id"/>
<group expand="0" string="Extended Filters..."> <group expand="0" string="Extended Filters...">

View File

@ -17,33 +17,20 @@
!assert {model: hr.expense.expense, id: sep_expenses, severity: error, string: Expense should be in Approved state}: !assert {model: hr.expense.expense, id: sep_expenses, severity: error, string: Expense should be in Approved state}:
- state == 'accepted' - state == 'accepted'
- -
I make Invoice for the expense. I make Receipt for the expense.
- -
!python {model: hr.expense.expense}: | !workflow {model: hr.expense.expense, action: done, ref: sep_expenses}
self.invoice(cr, uid, [ref('sep_expenses')])
- -
I check invoice details. I check receipt details.
- -
!python {model: hr.expense.expense}: | !python {model: hr.expense.expense}: |
sep_expenses = self.browse(cr, uid, ref("sep_expenses"), context=context) sep_expenses = self.browse(cr, uid, ref("sep_expenses"), context=context)
assert sep_expenses.state == 'invoiced', "Expense should be in 'Invoiced' state." assert sep_expenses.state == 'done', "Expense should be in 'Done' state."
assert sep_expenses.invoice_id, "Expense should have link of Invoice." assert sep_expenses.voucher_id, "Expense should have link of Purchase Receipt."
assert sep_expenses.invoice_id.currency_id == sep_expenses.currency_id,"Invoice currency is not correspond with supplier invoice currency" assert sep_expenses.voucher_id.type == 'purchase', "Receipt type is not purchase receipt."
assert sep_expenses.invoice_id.origin == sep_expenses.name,"Invoice origin is not correspond with supplier invoice" assert sep_expenses.voucher_id.amount == sep_expenses.amount,"Receipt total amount is not correspond with expense total."
assert sep_expenses.invoice_id.type == 'in_invoice', "Invoice type is not supplier invoice" assert len(sep_expenses.voucher_id.line_dr_ids) == len(sep_expenses.line_ids),"Lines of Receipt and expense line are not correspond."
assert sep_expenses.invoice_id.amount_total == sep_expenses.amount,"Invoice total amount is not correspond with supplier invoice total"
assert len(sep_expenses.invoice_id.invoice_line) == len(sep_expenses.line_ids),"Lines of Invoice and supplier invoice Line are not correspond"
#TODO: check invoice line details with Expenses lines
-
I pay the expenses.
-
!python {model: hr.expense.expense}: |
self.expense_paid(cr, uid, [ref('sep_expenses')])
-
I check that state of expenses is 'Paid'.
-
!assert {model: hr.expense.expense, id: sep_expenses, severity: error, string: Expense should be in Paid state}:
- state == 'paid'
- -
I duplicate the expenses and cancel duplicated. I duplicate the expenses and cancel duplicated.
- -

View File

@ -274,7 +274,7 @@ class hr_holidays(osv.osv):
'date': record.date_from, 'date': record.date_from,
'end_date': record.date_to, 'end_date': record.date_to,
'date_deadline': record.date_to, 'date_deadline': record.date_to,
'state': 'done', # to block that meeting date in the calendar 'state': 'open', # to block that meeting date in the calendar
} }
meeting_id = meeting_obj.create(cr, uid, meeting_vals) meeting_id = meeting_obj.create(cr, uid, meeting_vals)
self._create_resource_leave(cr, uid, [record], context=context) self._create_resource_leave(cr, uid, [record], context=context)

View File

@ -1,7 +1,8 @@
<?xml version="1.0" ?> <?xml version="1.0" ?>
<openerp> <openerp>
<data noupdate="1"> <data noupdate="1">
<!-- notify all employees of module installation -->
<!-- Notify all employees of module installation -->
<function model="mail.group" name="message_append_note"> <function model="mail.group" name="message_append_note">
<!-- ids, subject, body, parent_id=False, type='notification', content_subtype='html' --> <!-- ids, subject, body, parent_id=False, type='notification', content_subtype='html' -->
<value eval="[ref('mail.group_all_employees')]"/> <value eval="[ref('mail.group_all_employees')]"/>
@ -20,13 +21,7 @@
<field eval="&quot;&quot;&quot;My Timesheet&quot;&quot;&quot;" name="name"/> <field eval="&quot;&quot;&quot;My Timesheet&quot;&quot;&quot;" name="name"/>
</record> </record>
<record id="menu_act_hr_timesheet_sheet_form_my_current" model="ir.ui.menu"> <menuitem name="My Current Timesheet" id="menu_act_hr_timesheet_sheet_form_my_current" parent="hr_attendance.menu_hr_time_tracking" action="ir_actions_server_timsheet_sheet" sequence="1"/>
<field name="name">My Current Timesheet</field>
<field eval="1" name="sequence"/>
<field name="parent_id" ref="hr_attendance.menu_hr_time_tracking"/>
<field name="icon">STOCK_JUSTIFY_FILL</field>
<field name="action" ref="ir_actions_server_timsheet_sheet"/>
</record>
</data> </data>
</openerp> </openerp>

View File

@ -25,7 +25,7 @@
- -
!record {model: account.invoice.line, id: dta_invoice_line, view: False}: !record {model: account.invoice.line, id: dta_invoice_line, view: False}:
account_id: account.a_expense account_id: account.a_expense
name: '[PC1] Basic PC' name: '[PCSC234] PC Assemble SC234'
price_unit: 700.0 price_unit: 700.0
quantity: 10.0 quantity: 10.0
product_id: product.product_product_3 product_id: product.product_product_3

View File

@ -26,7 +26,7 @@
- -
!record {model: account.invoice.line, id: v11_test_invoice_line, view: False}: !record {model: account.invoice.line, id: v11_test_invoice_line, view: False}:
account_id: account.a_expense account_id: account.a_expense
name: '[PC1] Basic PC' name: '[PCSC234] PC Assemble SC234'
price_unit: 888.00 price_unit: 888.00
quantity: 1.0 quantity: 1.0
product_id: product.product_product_3 product_id: product.product_product_3

View File

@ -27,7 +27,7 @@
- -
!record {model: account.invoice.line, id: v11_part_test_invoice_line, view: False}: !record {model: account.invoice.line, id: v11_part_test_invoice_line, view: False}:
account_id: account.a_expense account_id: account.a_expense
name: '[PC1] Basic PC' name: '[PCSC234] PC Assemble SC234'
price_unit: 250.00 price_unit: 250.00
quantity: 1.0 quantity: 1.0
product_id: product.product_product_3 product_id: product.product_product_3

View File

@ -8,21 +8,11 @@
groups="base.group_user" groups="base.group_user"
sequence="10"/> sequence="10"/>
<!-- left-side menu: Feeds !--> <!-- Left-side menu: Feeds -->
<menuitem id="mail_feeds" name="Feeds" parent="mail_feeds_main" groups="base.group_user" sequence="10"/> <menuitem id="mail_feeds" name="Feeds" parent="mail_feeds_main" groups="base.group_user" sequence="10"/>
<record id="mail_wallfeeds" model="ir.ui.menu"> <menuitem id="mail_wallfeeds" name="News Feed" parent="mail_feeds" action="action_mail_all_feeds" sequence="10"/>
<field name="name">News Feed</field> <menuitem id="mail_myfeeds" name="My Feeds" parent="mail_feeds" action="action_mail_my_feeds" sequence="11"/>
<field name="sequence" eval="10"/>
<field name="action" ref="action_mail_all_feeds"/>
<field name="parent_id" ref="mail_feeds"/>
</record>
<record id="mail_myfeeds" model="ir.ui.menu">
<field name="name">My Feeds</field>
<field name="sequence" eval="11"/>
<field name="action" ref="action_mail_my_feeds"/>
<field name="parent_id" ref="mail_feeds"/>
</record>
</data> </data>
</openerp> </openerp>

View File

@ -6,9 +6,10 @@
<field name="model">base.config.settings</field> <field name="model">base.config.settings</field>
<field name="inherit_id" ref="base_setup.view_general_configuration"/> <field name="inherit_id" ref="base_setup.view_general_configuration"/>
<field name="arch" type="xml"> <field name="arch" type="xml">
<xpath expr="/form/group[last()]/div[last()]/div[last()]" position='after' version="7.0"> <xpath expr="//div[@name='email']" position='inside'>
<div> <div>
<label for="alias_domain" class="oe_inline"/><field name="alias_domain" placeholder="mycompany.my.openerp.com" class="oe_inline"/> <label for="alias_domain" class="oe_inline"/>
<field name="alias_domain" placeholder="mycompany.my.openerp.com" class="oe_inline"/>
</div> </div>
</xpath> </xpath>
</field> </field>

View File

@ -838,13 +838,13 @@ openerp.mail = function(session) {
init: function() { init: function() {
this._super.apply(this, arguments); this._super.apply(this, arguments);
this.params = this.get_definition_options(); this.params = this.options;
this.params.thread_level = this.params.thread_level || 0; this.params.thread_level = this.params.thread_level || 0;
this.thread = null; this.thread = null;
this.ds = new session.web.DataSet(this, this.view.model); this.ds = new session.web.DataSet(this, this.view.model);
this.ds_users = new session.web.DataSet(this, 'res.users'); this.ds_users = new session.web.DataSet(this, 'res.users');
}, },
start: function() { start: function() {
// NB: all the widget should be modified to check the actual_mode property on view, not use // NB: all the widget should be modified to check the actual_mode property on view, not use
// any other method to know if the view is in create mode anymore // any other method to know if the view is in create mode anymore
@ -858,7 +858,6 @@ openerp.mail = function(session) {
this.$element.find('button.oe_mail_button_unfollow').click(function () { self.do_unfollow(); }) this.$element.find('button.oe_mail_button_unfollow').click(function () { self.do_unfollow(); })
.mouseover(function () { $(this).html('Unfollow').removeClass('oe_mail_button_mouseout').addClass('oe_mail_button_mouseover'); }) .mouseover(function () { $(this).html('Unfollow').removeClass('oe_mail_button_mouseout').addClass('oe_mail_button_mouseover'); })
.mouseleave(function () { $(this).html('Following').removeClass('oe_mail_button_mouseover').addClass('oe_mail_button_mouseout'); }); .mouseleave(function () { $(this).html('Following').removeClass('oe_mail_button_mouseover').addClass('oe_mail_button_mouseout'); });
this.reinit();
}, },
_check_visibility: function() { _check_visibility: function() {

View File

@ -5,7 +5,7 @@
date_expected: !eval datetime.today().strftime("%Y-%m-%d %H:%M:%S") date_expected: !eval datetime.today().strftime("%Y-%m-%d %H:%M:%S")
location_dest_id: stock.stock_location_14 location_dest_id: stock.stock_location_14
location_id: stock.stock_location_stock location_id: stock.stock_location_stock
name: '[PC1] Basic PC' name: '[PCSC234] PC Assemble SC234'
product_id: product.product_product_3 product_id: product.product_product_3
product_qty: 1.0 product_qty: 1.0
product_uom: product.product_uom_unit product_uom: product.product_uom_unit
@ -23,7 +23,7 @@
operations: operations:
- location_dest_id: stock.location_production - location_dest_id: stock.location_production
location_id: stock.stock_location_stock location_id: stock.stock_location_stock
name: '[HDD1] HDD Seagate 7200.8 80GB' name: '[M-Las] Mouse, Laser'
price_unit: 50.0 price_unit: 50.0
product_id: product.product_product_11 product_id: product.product_product_11
product_uom: product.product_uom_unit product_uom: product.product_uom_unit
@ -32,7 +32,7 @@
to_invoice: 1 to_invoice: 1
type: add type: add
fees_lines: fees_lines:
- name: 'HDD1 Seagate repair fees' - name: 'Mouse repair fees'
product_id: product.product_product_11 product_id: product.product_product_11
product_uom_qty: 1.0 product_uom_qty: 1.0
product_uom: product.product_uom_unit product_uom: product.product_uom_unit
@ -46,7 +46,7 @@
date_expected: !eval datetime.today().strftime("%Y-%m-%d %H:%M:%S") date_expected: !eval datetime.today().strftime("%Y-%m-%d %H:%M:%S")
location_dest_id: stock.stock_location_14 location_dest_id: stock.stock_location_14
location_id: stock.stock_location_stock location_id: stock.stock_location_stock
name: '[PC3] Medium PC' name: '[PC-DEM] PC on Demand'
product_id: product.product_product_5 product_id: product.product_product_5
product_qty: 1.0 product_qty: 1.0
product_uom: product.product_uom_unit product_uom: product.product_uom_unit
@ -64,7 +64,7 @@
operations: operations:
- location_dest_id: stock.location_production - location_dest_id: stock.location_production
location_id: stock.stock_location_stock location_id: stock.stock_location_stock
name: '[HDD2] HDD Seagate 7200.8 120GB' name: '[M-Wir] Mouse, Wireless'
price_unit: 50.0 price_unit: 50.0
product_id: product.product_product_12 product_id: product.product_product_12
product_uom: product.product_uom_unit product_uom: product.product_uom_unit
@ -73,7 +73,7 @@
to_invoice: 1 to_invoice: 1
type: add type: add
fees_lines: fees_lines:
- name: 'HDD2 Seagate repair fees' - name: 'Mouse Seagate repair fees'
product_id: product.product_product_12 product_id: product.product_product_12
product_uom_qty: 1.0 product_uom_qty: 1.0
product_uom: product.product_uom_unit product_uom: product.product_uom_unit
@ -87,7 +87,7 @@
date_expected: !eval datetime.today().strftime("%Y-%m-%d %H:%M:%S") date_expected: !eval datetime.today().strftime("%Y-%m-%d %H:%M:%S")
location_dest_id: stock.stock_location_14 location_dest_id: stock.stock_location_14
location_id: stock.stock_location_stock location_id: stock.stock_location_stock
name: '[PC4] Customizable PC' name: '[LCD15] 15” LCD Monitor'
product_id: product.product_product_6 product_id: product.product_product_6
product_qty: 1.0 product_qty: 1.0
product_uom: product.product_uom_unit product_uom: product.product_uom_unit
@ -105,7 +105,7 @@
operations: operations:
- location_dest_id: stock.location_production - location_dest_id: stock.location_production
location_id: stock.stock_location_stock location_id: stock.stock_location_stock
name: '[HDD3] HDD Seagate 7200.8 160GB' name: '[RAM-SR5] RAM DDR SR5'
price_unit: 50.0 price_unit: 50.0
product_id: product.product_product_13 product_id: product.product_product_13
product_uom: product.product_uom_unit product_uom: product.product_uom_unit
@ -114,7 +114,7 @@
to_invoice: 1 to_invoice: 1
type: add type: add
fees_lines: fees_lines:
- name: 'HDD3 Seagate repair fees' - name: 'RAM fees'
product_id: product.product_product_13 product_id: product.product_product_13
product_uom_qty: 1.0 product_uom_qty: 1.0
product_uom: product.product_uom_unit product_uom: product.product_uom_unit

View File

@ -1,6 +1,6 @@
- -
In order to test the cancel flow of mrp_repair module, In order to test the cancel flow of mrp_repair module,
I start by creating new copy Repair order for "Basic PC" product. I start by creating new copy Repair order for "PC Assemble SC234" product.
- -
!python {model: mrp.repair}: | !python {model: mrp.repair}: |
copy_id = self.copy(cr, uid, ref("mrp_repair_rmrp1")) copy_id = self.copy(cr, uid, ref("mrp_repair_rmrp1"))

View File

@ -8,14 +8,14 @@ msgstr ""
"Project-Id-Version: openobject-addons\n" "Project-Id-Version: openobject-addons\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" "Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2012-02-08 00:36+0000\n" "POT-Creation-Date: 2012-02-08 00:36+0000\n"
"PO-Revision-Date: 2010-10-26 08:32+0000\n" "PO-Revision-Date: 2012-08-17 11:09+0000\n"
"Last-Translator: Chertykov Denis <chertykov@gmail.com>\n" "Last-Translator: Chertykov Denis <chertykov@gmail.com>\n"
"Language-Team: Russian <ru@li.org>\n" "Language-Team: Russian <ru@li.org>\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2012-08-07 05:25+0000\n" "X-Launchpad-Export-Date: 2012-08-18 04:58+0000\n"
"X-Generator: Launchpad (build 15745)\n" "X-Generator: Launchpad (build 15810)\n"
#. module: multi_company #. module: multi_company
#: model:res.company,overdue_msg:multi_company.res_company_odoo #: model:res.company,overdue_msg:multi_company.res_company_odoo
@ -68,7 +68,7 @@ msgstr "Возвращаемое"
#. module: multi_company #. module: multi_company
#: model:ir.ui.menu,name:multi_company.menu_custom_multicompany #: model:ir.ui.menu,name:multi_company.menu_custom_multicompany
msgid "Multi-Companies" msgid "Multi-Companies"
msgstr "" msgstr "Холдинги"
#. module: multi_company #. module: multi_company
#: view:multi_company.default:0 #: view:multi_company.default:0

View File

@ -79,18 +79,17 @@ Main features:
], ],
'installable': True, 'installable': True,
'application': True, 'application': True,
# Web client
'js': [ 'js': [
'static/lib/backbone/backbone-0.9.2.js',
'static/lib/mousewheel/jquery.mousewheel-3.0.6.js', 'static/lib/mousewheel/jquery.mousewheel-3.0.6.js',
'static/src/js/pos_models.js', 'static/src/js/db.js',
'static/src/js/pos_basewidget.js', 'static/src/js/models.js',
'static/src/js/pos_keyboard_widget.js', 'static/src/js/widget_base.js',
'static/src/js/pos_scrollbar_widget.js', 'static/src/js/widget_keyboard.js',
'static/src/js/pos_widgets.js', 'static/src/js/widget_scrollbar.js',
'static/src/js/pos_devices.js', 'static/src/js/widgets.js',
'static/src/js/pos_screens.js', 'static/src/js/devices.js',
'static/src/js/pos_main.js' 'static/src/js/screens.js',
'static/src/js/main.js',
], ],
'css': [ 'css': [
'static/src/css/pos.css', 'static/src/css/pos.css',
@ -99,4 +98,5 @@ Main features:
'qweb': ['static/src/xml/pos.xml'], 'qweb': ['static/src/xml/pos.xml'],
'auto_install': False, 'auto_install': False,
} }
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -34,8 +34,8 @@ class account_journal(osv.osv):
'self_checkout_payment_method' : fields.boolean('Self Checkout Payment Method'), 'self_checkout_payment_method' : fields.boolean('Self Checkout Payment Method'),
} }
_defaults = { _defaults = {
'opening_control' : True, 'opening_control' : False,
'closing_control' : True, 'closing_control' : False,
'self_checkout_payment_method' : False, 'self_checkout_payment_method' : False,
} }

View File

@ -0,0 +1,3 @@
import main
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -1,14 +1,71 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import logging import logging
import simplejson
import os
import openerp
try: try:
import openerp.addons.web.common.http as openerpweb import openerp.addons.web.common.http as openerpweb
from openerp.addons.web.controllers.main import manifest_list, module_boot, html_template
except ImportError: except ImportError:
import web.common.http as openerpweb import web.common.http as openerpweb
class PointOfSaleController(openerpweb.Controller): class PointOfSaleController(openerpweb.Controller):
_cp_path = '/pos' _cp_path = '/pos'
@openerpweb.httprequest
def app(self, req, s_action=None, **kw):
js = "\n ".join('<script type="text/javascript" src="%s"></script>' % i for i in manifest_list(req, None, 'js'))
css = "\n ".join('<link rel="stylesheet" href="%s">' % i for i in manifest_list(req, None, 'css'))
cookie = req.httprequest.cookies.get("instance0|session_id")
session_id = cookie.replace("%22","")
template = html_template.replace('<html','<html manifest="/pos/manifest?session_id=%s"'%session_id)
r = template % {
'js': js,
'css': css,
'modules': simplejson.dumps(module_boot(req)),
'init': 'var wc = new s.web.WebClient();wc.appendTo($(document.body));'
}
return r
@openerpweb.httprequest
def manifest(self, req, **kwargs):
""" This generates a HTML5 cache manifest files that preloads the categories and products thumbnails
and other ressources necessary for the point of sale to work offline """
ml = ["CACHE MANIFEST"]
# loading all the images in the static/src/img/* directories
def load_css_img(srcdir,dstdir):
for f in os.listdir(srcdir):
path = os.path.join(srcdir,f)
dstpath = os.path.join(dstdir,f)
if os.path.isdir(path) :
load_css_img(path,dstpath)
elif f.endswith(('.png','.PNG','.jpg','.JPG','.jpeg','.JPEG','.gif','.GIF')):
ml.append(dstpath)
imgdir = openerp.modules.get_module_resource('point_of_sale','static/src/img');
load_css_img(imgdir,'/point_of_sale/static/src/img')
products = req.session.model('product.product')
for p in products.search_read([('pos_categ_id','!=',False)], ['name']):
product_id = p['id']
url = "/web/binary/image?session_id=%s&model=product.product&field=image&id=%s" % (req.session_id, product_id)
ml.append(url)
categories = req.session.model('pos.category')
for c in categories.search_read([],['name']):
category_id = c['id']
url = "/web/binary/image?session_id=%s&model=pos.category&field=image&id=%s" % (req.session_id, category_id)
ml.append(url)
ml += ["NETWORK:","*"]
m = "\n".join(ml)
return m
@openerpweb.jsonrequest @openerpweb.jsonrequest
def dispatch(self, request, iface, **kwargs): def dispatch(self, request, iface, **kwargs):
method = 'iface_%s' % iface method = 'iface_%s' % iface
@ -109,3 +166,4 @@ class PointOfSaleController(openerpweb.Controller):
print 'print_receipt' + str(receipt) print 'print_receipt' + str(receipt)
return return

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 75 KiB

View File

@ -0,0 +1,506 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="744.09448819"
height="1052.3622047"
id="svg2"
version="1.1"
inkscape:version="0.48.3.1 r9886"
sodipodi:docname="barcode_test_sheet.svg">
<defs
id="defs4" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="1"
inkscape:pageshadow="2"
inkscape:zoom="0.86706515"
inkscape:cx="381.55817"
inkscape:cy="597.57819"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="1301"
inkscape:window-height="744"
inkscape:window-x="1985"
inkscape:window-y="24"
inkscape:window-maximized="1" />
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<g
id="g4158"
transform="matrix(0.92675002,0,0,0.92675002,27.597551,38.360312)">
<text
sodipodi:linespacing="125%"
id="text2985"
y="72.12545"
x="140.86679"
style="font-size:46.51613998px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
xml:space="preserve"><tspan
style="font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;font-family:Inconsolata;-inkscape-font-specification:Inconsolata Medium"
y="72.12545"
x="140.86679"
id="tspan2987"
sodipodi:role="line">BARCODE TEST SHEET</tspan></text>
<g
transform="translate(0,-5.3405762e-5)"
id="g3347">
<image
y="124.14642"
x="388.17426"
id="image3083"
xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAOYAAACYCAIAAAB/IAJdAAAAA3NCSVQICAjb4U/gAAADqUlEQVR4 nO3a226jMABFUYjy/7/MPCChDBfHARM4zVpP1dQY4u66JEw/DEO3S9/3XdeNh79+vRwz2hr5OmZr 5JH5t0YuX0X51W0dW3PNNa9xOWf5au8wsrxu9Ud95HHkYPg+yRJGsoSRLGEkSxjJEkayhJEsYSRL GMkSRrKEkSxhJEsYyRJGsoSRLGEkSxjJEkayhJEsYSRLGMkSRrKEkSxhJEsYyRJGsoSRLGEkSxjJ EkayhJEsYSRLGMkSRrKEkSxhJEsYyRJGsoSRLGEkSxjJEkayhJEsYSRLGMkSRrKEkSxhJEsYyRJG soSRLGEkSxjJEkayhJEsYSRLGMkSRrKEkSxhJEsYyRJGsoSRLGEkSxjJEkayhJEsYSRLGMkSRrKE kSxhJEsYyRJGsoSRLGEkSxjJEkayhJEsYSRLGMkSRrKEkSxhJEsYyRJGsoSRLGEkSxjJEkayhJEs YSRLGMkSRrKEkSxhJEsYyRJGsoSRLGEkSxjJEkayhJEsYSRLGMkSRrKEkSxhJEsYyRJGsoSRLGEk SxjJEkayhJEsYSRLGMkSRrKEkSxhJEsYyRJGsoSRLGEkSxjJEkayhJEsYSRLGMkSRrKEkSxh+mEY dh7Z913XjYe/fv3LrMlS8zWxyxJGsjuNGwbf91z911Z/1GY/18oJ689+cOTb7LZmPtJr/ZrcfOTu 1SucseaQlV221f6xnKdm5vqznzHy1Nn6vq9fk5SRrYzzry7RzHyXbd7r9HszXVP9b3bN/EdGlq/k 0y250uu0W2tSv3oXjvxo9d4ahmE6b/mP58ou2/BNbuUr3Hf2M0aOzthRVn8M5QurX71rR87sXr1h GGa/z6tT/Zds2y12+TpfP+nYOqp+/rYjZ4esXvzk0zkLlmtSv3rXjlx1/F3QMtzZgMfse1d9jvi1 t1zND2HScPUK2+2j+cl2uEmvl/Bh2Zat+4TH64gLruvzs599C3vSOqz+Yf1LvZ60estwn93ehdt9 ez5zk1vYJmrW5C9lWqNVJ5OnW4IjR31kudFWvqe5v/NWb/lE47n6jbOvo3xZhbOfMfLL7n97fRNb j9/WH9j+mgtTtsUW5hzNN6PVMx2/iMLO93ba798tfPp6d398tjyqfqFu+I9vv7XD1uY6Ofd/ci0/ o/5ZhR/2lvrVu3ZkK7PPX7ee2px1YzA9Mn77S/Nrlj/+1UdNlat37ciGCncCMyfuslsP/e7mazey y22j8Pi3fvWuHdlq9cYZap6H/wMqFm5yx9XfMwAAAABJRU5ErkJggg== "
height="107.96206"
width="163.36363" />
<rect
style="fill:none;stroke:#c3c3c3;stroke-width:1.07903957;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
id="rect3086"
width="417.54495"
height="126.4052"
x="141.08452"
y="114.51707"
rx="2.0839903"
ry="2.0839903" />
<text
xml:space="preserve"
style="font-size:27.53050041px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
x="201.46712"
y="147.73848"
id="text3856"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3858"
x="201.46712"
y="147.73848"
style="font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;font-family:Inconsolata;-inkscape-font-specification:Inconsolata Medium">CASHIER</tspan></text>
<text
sodipodi:linespacing="125%"
id="text3860"
y="182.10147"
x="154.5717"
style="font-size:17.41555405px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
xml:space="preserve"><tspan
style="font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;font-family:Inconsolata;-inkscape-font-specification:Inconsolata Medium"
y="182.10147"
x="154.5717"
id="tspan3862"
sodipodi:role="line">Prefix: 40</tspan></text>
<text
xml:space="preserve"
style="font-size:17.41555405px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
x="154.5717"
y="200.85837"
id="text3149"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3151"
x="154.5717"
y="200.85837"
style="font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;font-family:Inconsolata;-inkscape-font-specification:Inconsolata Medium">ID: 4447190000</tspan></text>
</g>
<g
transform="translate(0,0.97855377)"
id="g3335">
<g
id="g3173"
transform="translate(0,-3.2620699)">
<text
sodipodi:linespacing="125%"
id="text3153"
y="341.12738"
x="154.5717"
style="font-size:17.41555405px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
xml:space="preserve"><tspan
style="font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;font-family:Inconsolata;-inkscape-font-specification:Inconsolata Medium"
y="341.12738"
x="154.5717"
id="tspan3155"
sodipodi:role="line">Prefix: 42</tspan></text>
<text
xml:space="preserve"
style="font-size:17.41555405px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
x="154.5717"
y="359.88428"
id="text3157"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3159"
x="154.5717"
y="359.88428"
style="font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;font-family:Inconsolata;-inkscape-font-specification:Inconsolata Medium">ID: 1182180000</tspan></text>
</g>
<g
id="g3954"
transform="translate(0,-18.756902)">
<rect
style="fill:none;stroke:#c3c3c3;stroke-width:1.07903957;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
id="rect3876"
width="417.54495"
height="126.4052"
x="141.08452"
y="284.96024"
rx="2.0839903"
ry="2.0839903" />
<text
xml:space="preserve"
style="font-size:27.53050041px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
x="209.6223"
y="318.18164"
id="text3878"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3880"
x="209.6223"
y="318.18164"
style="font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;font-family:Inconsolata;-inkscape-font-specification:Inconsolata Medium">CLIENT</tspan></text>
<image
width="162.31204"
height="107.26709"
xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAOYAAACYCAIAAAB/IAJdAAAAA3NCSVQICAjb4U/gAAADyUlEQVR4 nO3a3Y6qShSFUTC+/yvXuSAxbCgQsPiZp8e46mgJq/ETxe6+lNId0vd913XDw8c/z+8d3zIYP2p8 y9LKLbcvbXN8y5Zt/jLn0jHZcnzm29z+m961cmn+vY/a5fXLg+F6kiWMZAkjWcJIljCSJYxkCSNZ wkiWMJIljGQJI1nCSJYwkiWMZAkjWcJIljCSJYxkCSNZwkiWMJIljGQJI1nCSJYwkiWMZAkjWcJI ljCSJYxkCSNZwkiWMJIljGQJI1nCSJYwkiWMZAkjWcJIljCSJYxkCSNZwkiWMJIljGQJI1nCSJYw kiWMZAkjWcJIljCSJYxkCSNZwkiWMJIljGQJI1nCSJYwkiWMZAkjWcJIljCSJYxkCSNZwkiWMJIl jGQJI1nCSJYwkiWMZAkjWcJIljCSJYxkCSNZwkiWMJIljGQJI1nCSJYwkiWMZAkjWcJIljCSJYxk CSNZwkiWMJIljGQJI1nCSJYwkiWMZAkjWcJIljCSJYxkCSNZwkiWMJIljGQJI1nCSJYwkiWMZAkj WcJIljCSJYxkCSNZwkiWMJIljGQJI1nCSJYwkiWMZAkjWcJIljCSJYxkCSNZwkiWMJIljGQJI1nC 9KWUg4/s+67rhoePf/7LHJO55sfEWZYwj0t2eCG2XXmGe/f+l72rt7Z6U5s8r1832LbX+ZqlAc6b 85qpbln59SDs7afvN31MrZxlm5w/+r6fb6d644H9Huu1euOpcx6eqnrj0n7vXdnKsP31Iz+YnmWb TzZ+3Wx/wqoOrJzvfeml3HDO36ear1wa/saV62fovafYUspnv+tv8pWzbMPPA5NNjS8bL1Dd+9gt c36dqnrX+pNy78qJw8etlDJ53VY39U+ybZ+kjb9nGWm1cpfmc/5o6RwzfyHdu3LX8NvNw50seE3u a/J8XPC8bjH5bavXPdfP+XWqXG0TGm92fJTezXf2BJ8PRtsvzy/wzKkea3Je/yT6mqw41e0vjI27 vnjO/0evJx20+eeEd3f0vWnXx/OlC52TVA/f8P6yPsCPc64fk8NTRTt8GbfkffYZ5eJYu9ULiJU+ zp7z2FQpzqtocj4tpbyrdzSZY7zNJz8fKXP+NfNYhx/qf7BtuL+HR5Ay55OdcYpdOY+8Ss1n6Y9z pHTwqDmXvgGdl3HvypNMvtKa7/GU/+S695uB7Qf9yjkPpDD/hn/JvStbmcdaPSxnfTDoVn/PK783 +GXZBdcTS/utfoO7dPV218qGtl9RPO7/ZZuovkBv/5vcrqmqK5c2e+PKVu9Uwxa2PEf/AXnknj+O sxSLAAAAAElFTkSuQmCC "
id="image3951"
x="388.17426"
y="294.58957" />
</g>
</g>
<g
transform="translate(0,0.3261795)"
id="g3322">
<rect
style="fill:none;stroke:#c3c3c3;stroke-width:1.07903957;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
id="rect3964"
width="417.54495"
height="126.4052"
x="141.08452"
y="419.52063"
rx="2.0839903"
ry="2.0839903" />
<text
xml:space="preserve"
style="font-size:27.53050041px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
x="209.6223"
y="452.74203"
id="text3966"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3968"
x="209.6223"
y="452.74203"
style="font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;font-family:Inconsolata;-inkscape-font-specification:Inconsolata Medium">WEIGHT</tspan></text>
<image
width="162.12962"
height="107.14654"
xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAOYAAACYCAIAAAB/IAJdAAAAA3NCSVQICAjb4U/gAAAERElEQVR4 nO3a0ZKaQBRFUbX8/182D1QZInRzadDxTNZ6mjgNNrhBYHJ9PB6XIdfr9XK5TIvPf57/drJ8/ayR rVdaG9Wfc+Vd+vPc+3N/hv3XK68st2i5Xa09XNnS+sjKthfdjiwMnydZwkiWMJIljGQJI1nCSJYw kiWMZAkjWcJIljCSJYxkCSNZwkiWMJIljGQJI1nCSJYwkiWMZAkjWcJIljCSJYxkCSNZwkiWMJIl jGQJI1nCSJYwkiWMZAkjWcJIljCSJYxkCSNZwkiWMJIljGQJI1nCSJYwkiWMZAkjWcJIljCSJYxk CSNZwkiWMJIljGQJI1nCSJYwkiWMZAkjWcJIljCSJYxkCSNZwkiWMJIljGQJI1nCSJYwkiWMZAkj WcJIljCSJYxkCSNZwkiWMJIljGQJI1nCSJYwkiWMZAkjWcJIljCSJYxkCSNZwkiWMJIljGQJI1nC SJYwkiWMZAkjWcJIljCSJYxkCSNZwkiWMJIljGQJI1nCSJYwkiWMZAkjWcJIljCSJYxkCSNZwkiW MJIljGQJI1nCSJYwkiWMZAkjWcJIljCSJYxkCSNZwkiWMJIljGQJI1nCSJYwkiWMZAkjWcJIljCS Jcz18XgMLnm9Xi6XafH5z/8z+2Tp9H3iLEuYDyU7HV7njuT/dH/+9NLKid9o5/Zan+fYFnW+vFrT 27uv+hPb3AnLt1su0prSO/beWeVcr6XL1Ns0dLnNqy+OzeOskfV5Dm9RZ8BZe2N1YgfXWXlx11u/ Y2TftFTlM7rP/zFv/B378eDIp+I8V89enUO5MpMjXz7P9S/nP59V/3zWOiX317k6/9aw5TqPj9z0 eDyea+vfpd1Wt/ny7y3eN6jP88jX9wdu8Jfzn2wuWD84W1cOm8Navz1rZN/LfmidcW/H36nz9pUP oz7yUs6uuLa5dx+cB5/vvOmRWeuAb52ex0bunVL/W/Se8uDw+Dw3r1MHTnVjs5qvpP6m9QuG7/lu HNa5Trg3F/oVKrfSR05ge5dt3awMH5DPj7b+0KA/n+/xcrZ+7qhmsm/6JjpdfZ6dMZuLd+5yBuxa ydgHsbxsnS4N407Gy3BXkm3d6KyuqzLyTSrzXH5Cex+Cdt5ifoabBlT2SSWmutZGtVb4+UxP7+Sf v34979EGbl8+aWye9RuIuoMnv039Ge66AVruqy//lCcvzw3+XhjsvSH4Ke+YZ+dR/DfvijFBW9S6 071fylcCP644z19T2wc25GsvZzvnpnvrF1/rxHl2rvZWL3lbTxuK77V6ifmZaPpH+8vlZmeeYyPH ptqa8C3lnPTjj5OW6xz7dlr9Q93YrFoP7Vfz6s+kOM+DI/uWl63r095c0VnP8A8+M9/cF8XHN8Xn WfUT6nCvm2uo7Lf6lu6af32eu7aoo36X8jv/i/fqAXrwBrm1zr0rKa6heJzXt3TXE4P6PI/vk/lS lc/oD6RX6TSeNJuDAAAAAElFTkSuQmCC "
id="image4065"
x="387.35876"
y="429.14993" />
<g
transform="translate(0,140.26901)"
id="g3179">
<text
xml:space="preserve"
style="font-size:17.41555405px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
x="154.5717"
y="341.12738"
id="text3181"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3183"
x="154.5717"
y="341.12738"
style="font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;font-family:Inconsolata;-inkscape-font-specification:Inconsolata Medium">Prefix: 21</tspan></text>
<text
sodipodi:linespacing="125%"
id="text3185"
y="359.88428"
x="154.5717"
style="font-size:17.41555405px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
xml:space="preserve"><tspan
style="font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;font-family:Inconsolata;-inkscape-font-specification:Inconsolata Medium"
y="359.88428"
x="154.5717"
id="tspan3187"
sodipodi:role="line">ID: 12345</tspan></text>
<text
xml:space="preserve"
style="font-size:17.41555405px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
x="154.5717"
y="379.4567"
id="text3189"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3191"
x="154.5717"
y="379.4567"
style="font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;font-family:Inconsolata;-inkscape-font-specification:Inconsolata Medium">Value: 67.89Kg</tspan></text>
</g>
</g>
<g
transform="translate(0,-1.9572678)"
id="g3308">
<rect
ry="2.0839903"
rx="2.0839903"
y="574.46893"
x="141.08452"
height="126.4052"
width="417.54495"
id="rect4081"
style="fill:none;stroke:#c3c3c3;stroke-width:1.07903957;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
<g
id="g3296">
<text
xml:space="preserve"
style="font-size:27.53050041px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
x="195.7585"
y="607.69037"
id="text4083"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan4085"
x="195.7585"
y="607.69037"
style="font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;font-family:Inconsolata;-inkscape-font-specification:Inconsolata Medium">DISCOUNT</tspan></text>
<image
y="584.09827"
x="385.72772"
id="image4202"
xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAOYAAACYCAIAAAB/IAJdAAAAA3NCSVQICAjb4U/gAAADsElEQVR4 nO3aXXOiMACGUej4//8ye+EMZYHECBF96zlXHTd8iE8jYTtO0zQcMo7jMAz3zUs/b8ffLUcuXymN bHn92ZHtR3l2ZOnnpfazOvauS3toP3rLyPr5tGx1wM+ZjeF6kiWMZAkjWcJIljCSJYxkCSNZwkiW MJIljGQJI1nCSJYwkiWMZAkjWcJIljCSJYxkCSNZwkiWMJIljGQJI1nCSJYwkiWMZAkjWcJIljCS JYxkCSNZwkiWMJIljGQJI1nCSJYwkiWMZAkjWcJIljCSJYxkCSNZwkiWMJIljGQJI1nCSJYwkiWM ZAkjWcJIljCSJYxkCSNZwkiWMJIljGQJI1nCSJYwkiWMZAkjWcJIljCSJYxkCSNZwkiWMJIljGQJ I1nCSJYwkiWMZAkjWcJIljCSJYxkCSNZwkiWMJIljGQJI1nCSJYwkiWMZAkjWcJIljCSJYxkCSNZ wkiWMJIljGQJI1nCSJYwkiWMZAkjWcJIljCSJYxkCSNZwkiWMJIljGQJI1nCSJYwkiWMZAkjWcJI ljCSJYxkCSNZwkiWMJIljGQJI1nCSJYwkiWMZAkjWcJIljCSJYxkCSNZwkiWMJIljGQJI1nCjNM0 HdxyHIdhuG++/PmbuSZb3a+JWZYwH5fs/RfxL42kr9vuq6/4UmvZZ0qFZ3pdbVu5II0jSyezO777 0XdP4Fg549h0m7ozy75i/mjZZ0qFHXst7W0cx92RjZufPPrJkc+e1XKT3fe4sp5lr+81pb/zV2be wzyXzJ9TaXZZvl45gZbJqf3oZ0ZWBtdN0zTvrf6FvDPLvmKRa+E8W16K+nf36l+Xi+7XHf3kyIeD 6/tZpb/7Zv+bZd9yS9A4kQSN3FWaOe6zy3ZOumYhsT36s+fZ3eo3c3vE32TfteRi5cDlal8qvcKq qi4TX+U+4bb7ahd6fZfLrvwcVq+HBts9bMO9rUb0pdeOtiFWVj/v1fFz34Z7G46+yfrt+SdcuOu1 L1meUl+Qbc/h2Jr98IltHyMsD939mvy4Jfhk86p5tZquu+bKVxZqQ785a/Xc4PfGoPJA+OTfzfTd 5/dYfU5vPJN3Ka0p9//Dlvcq3Ql8j8pv7K3yNPvMM+Hu+0xXeq5ZuSwPr1Vp2931e+PRD5xnXw8f 2H3cX3L9ecuPpMu902on9Rn64dHbR5buWQ/Hvb1t3d2JG4PrlJ5itvzJ1e74px6Lth/9qfOsn2q7 9nt3s+ylSuvrMzts32f3kaWjH3hT901atv0Hm+wpnHOLKX0AAAAASUVORK5CYII= "
height="108.34499"
width="163.94308"
inkscape:transform-center-x="-24.4145"
inkscape:transform-center-y="6.3942733" />
<g
transform="translate(0,296.03285)"
id="g3210">
<text
xml:space="preserve"
style="font-size:17.41555405px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
x="154.5717"
y="341.12738"
id="text3212"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3214"
x="154.5717"
y="341.12738"
style="font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;font-family:Inconsolata;-inkscape-font-specification:Inconsolata Medium">Prefix: 44</tspan></text>
<text
sodipodi:linespacing="125%"
id="text3216"
y="359.88428"
x="154.5717"
style="font-size:17.41555405px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
xml:space="preserve"><tspan
style="font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;font-family:Inconsolata;-inkscape-font-specification:Inconsolata Medium"
y="359.88428"
x="154.5717"
id="tspan3218"
sodipodi:role="line">ID: 11111</tspan></text>
<text
xml:space="preserve"
style="font-size:17.41555405px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
x="154.5717"
y="379.4567"
id="text3220"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3222"
x="154.5717"
y="379.4567"
style="font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;font-family:Inconsolata;-inkscape-font-specification:Inconsolata Medium">Value: 25%</tspan></text>
</g>
</g>
</g>
<g
transform="translate(0,-0.97866058)"
id="g3283">
<rect
style="fill:none;stroke:#c3c3c3;stroke-width:1.07903957;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
id="rect4115"
width="417.54495"
height="126.4052"
x="141.08452"
y="726.15521"
rx="2.0839903"
ry="2.0839903" />
<text
xml:space="preserve"
style="font-size:27.53050041px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
x="212.88437"
y="759.37659"
id="text4117"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan4119"
x="212.88437"
y="759.37659"
style="font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;font-family:Inconsolata;-inkscape-font-specification:Inconsolata Medium">PRICE</tspan></text>
<image
width="163.36363"
height="107.96206"
xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAOYAAACYCAIAAAB/IAJdAAAAA3NCSVQICAjb4U/gAAAD70lEQVR4 nO3Z3XKbOhiGUezJ/d8yPfBsbwaB+Phz/aZrHaWukDB+goE8xnEcDnk8HsMwvDaf/tz+7/SVl3bR drbEkWvHZG3O/gytyvxrq6yt265emWfvyMq7K3qe2Rg+T7KEkSxhJEsYyRJGsoSRLGEkSxjJEkay hJEsYSRLGMkSRrKEkSxhJEsYyRJGsoSRLGEkSxjJEkayhJEsYSRLGMkSRrKEkSxhJEsYyRJGsoSR LGEkSxjJEkayhJEsYSRLGMkSRrKEkSxhJEsYyRJGsoSRLGEkSxjJEkayhJEsYSRLGMkSRrKEkSxh JEsYyRJGsoSRLGEkSxjJEkayhJEsYSRLGMkSRrKEkSxhJEsYyRJGsoSRLGEkSxjJEkayhJEsYSRL GMkSRrKEkSxhJEsYyRJGsoSRLGEkSxjJEkayhJEsYSRLGMkSRrKEkSxhJEsYyRJGsoSRLGEkSxjJ EkayhJEsYSRLGMkSRrKEkSxhJEsYyRJGsoSRLGEkSxjJEkayhJEsYSRLGMkSRrKEkSxhJEsYyRJG soSRLGEkSxjJEkayhJEsYSRLGMkSRrKEkSxhJEsYyRJGsoSRLGEkSxjJEkayhJEsYSRLmMc4jge3 fDyGYXhtPv35X+aYtC4/Js6yhJHsX/M65bDXz/un2RG85ButPufvG9lX6fXYWp0v37VFK4M/c0wq 2z4XV118Ze/yi3PWX6zP+W0jN21uVT9Q9Zl37Wr9nV57TCrv8We6xrvx9/bnz7XTGTp70w5bWz1l ZGvvZzmbdnO5yvyVXa33cGE54zi+Z+vfpT2n2yz+fED7Tqb/nB7ZxZ3rfE99/8jzdn19HxhTVO/h qnLGcZydERYPxXOt6DavA3twctv6aeP7Rw7/fSSzD6YzrDLn21U3c/UebiqnDXc24Ge4gYeRd9i8 Tt31pf/ytZ9U5zrhlmTPq/+Opow8pp2/jaze69r83xnu7Gz93tWPPpdtD9Dil8hiBykj79O5Wths bnp3cezC429prxOOn2V3XXSv3ZC1AzZ988gLb2HbSRbPiPXL6MUXL3w0VFn6klVuP8u+7/vWfrPb 19NH3qR+A7R3zm82e24wjuON17KzlfqD68cuZeTHrP3hY/jKva1bu1O8K9nNK4Hi5r9mZN0vqO28 zvnu53VBs/aHjZMH7uQ17uLzuS8f+UmL63YuedeeNszmLPZwUzmbj+Gei0NPnjzqO9056KEjr9JO fv4ksvbEY/Pd9d/pVeW0l63LO7b4+Oa9zeG1+wMWT2Cbq6eMrOj31zmAxS+uk/t/x294X/3O5/+z +oV/F9mVbLvJxh6HjOyrnDKPfSK7fhk6c94xsq/4rO0Pc0/7XuEpWE8AAAAASUVORK5CYII= "
id="image4264"
x="386.54324"
y="734.96906" />
<g
transform="translate(0,442.82599)"
id="g3224">
<text
xml:space="preserve"
style="font-size:17.41555405px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
x="154.5717"
y="341.12738"
id="text3226"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3228"
x="154.5717"
y="341.12738"
style="font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;font-family:Inconsolata;-inkscape-font-specification:Inconsolata Medium">Prefix: 02</tspan></text>
<text
sodipodi:linespacing="125%"
id="text3230"
y="359.88428"
x="154.5717"
style="font-size:17.41555405px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
xml:space="preserve"><tspan
style="font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;font-family:Inconsolata;-inkscape-font-specification:Inconsolata Medium"
y="359.88428"
x="154.5717"
id="tspan3232"
sodipodi:role="line">ID: 99999</tspan></text>
<text
xml:space="preserve"
style="font-size:17.41555405px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
x="154.5717"
y="379.4567"
id="text3234"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3236"
x="154.5717"
y="379.4567"
style="font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;font-family:Inconsolata;-inkscape-font-specification:Inconsolata Medium">Value: 134.50€</tspan></text>
</g>
</g>
<g
id="g3269">
<g
id="g3061"
transform="translate(0,284.6156)">
<g
id="g3063"
transform="translate(0,154.94832)">
<rect
ry="2.0839903"
rx="2.0839903"
y="438.27753"
x="141.08452"
height="126.4052"
width="417.54495"
id="rect3065"
style="fill:none;stroke:#c3c3c3;stroke-width:1.07903957;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
<text
sodipodi:linespacing="125%"
id="text3067"
y="471.49893"
x="216.61778"
style="font-size:27.53050041px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
xml:space="preserve"><tspan
style="font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;font-family:Inconsolata;-inkscape-font-specification:Inconsolata Medium"
y="471.49893"
x="216.61778"
sodipodi:role="line"
id="tspan3083">UNIT</tspan></text>
<g
transform="translate(0,156.57936)"
id="g3071" />
</g>
</g>
<image
y="887.47076"
x="386.54324"
id="image3146"
xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAOYAAACYCAIAAAB/IAJdAAAAA3NCSVQICAjb4U/gAAADz0lEQVR4 nO3Z0ZKaMACGUdnx/V+ZXjBjGWKyEQjyt+fcdIsBY/ZTgZ3meX7sMk3T4/FYdq/9XI5frB9db18/ Wh6nfJZyr/Yz9s+t/Yp65tw/22+N3Pe6asevhdQ/stPPkZ3hepIljGQJI1nCSJYwkiWMZAkjWcJI ljCSJYxkCSNZwkiWMJIljGQJI1nCSJYwkiWMZAkjWcJIljCSJYxkCSNZwkiWMJIljGQJI1nCSJYw kiWMZAkjWcJIljCSJYxkCSNZwkiWMJIljGQJI1nCSJYwkiWMZAkjWcJIljCSJYxkCSNZwkiWMJIl jGQJI1nCSJYwkiWMZAkjWcJIljCSJYxkCSNZwkiWMJIljGQJI1nCSJYwkiWMZAkjWcJIljCSJYxk CSNZwkiWMJIljGQJI1nCSJYwkiWMZAkjWcJIljCSJYxkCSNZwkiWMJIljGQJI1nCSJYwkiWMZAkj WcJIljCSJYxkCSNZwkiWMJIljGQJI1nCSJYwkiWMZAkjWcJIljCSJYxkCSNZwkiWMJIljGQJI1nC SJYwkiWMZAkjWcJIljCSJYxkCSNZwkiWMJIljGQJI1nCSJYwkiWMZAkjWcJIljCSJYxkCSNZwkzz PO/cc5oej8ey+/rn/5k1KZ2+Jj5lCSNZwjyXf5ZP7NKJ32v9XwrtkZupNg743ZH3nNWgV3p8WZaD 9Oz4Uz7fCP1P0Rg5TVP5aG38d0fWfHFW/av36TofX5bXLm+PtvF8/TTuQuGUXl/W83y9zs3kX8d5 bb9yZM0dZtWzev0jy2f/9bA18zy/jtb+mr3oXLZ/9u3zgc2j7cOuH71+5K1m1b96B9d5+e+isUvt OJv0336EDU/23I/Y0vruyfo45ZJdM7LmnrM6MnLQXbwy3M2AZ+2xU6Zy1iUXN7eO55RfYuM84Vnb 53hD1/R6wbXjP+z4d2Dt2ut4uJvP9ddhf9Zn0LvPQtrPesrIt19heu3Uv3r71nlEPOsjr2fyrF0t /nrd1z7lH3cKe+dMj1+cjTbi91Jefi1XTsv209ekevl1w1OC8k084m39r+pfvY/WefT6b+4bzPNc PZc95clqGzevs3/k2y30O/FUbbTa/YCByV7jzucJ9zf6FuRujVsQb/56sdnnrHfbwbOF2nwag7+1 ccfrGr2xf/XOWudye6dfb7b+vB13eq/HvZ1J+61f3ve+cuStZtW/emet845yytPW95NZ37PdOLfX 49dkH83zst/cR6v0xVn1r96gdW7r/2PE33tYI/76Vc7p+G2ED15b9ysaMfKes9q3emetc1vjjura Hwkp3Xa7HcbMAAAAAElFTkSuQmCC "
height="108.34499"
width="163.94308" />
<g
id="g3255"
transform="translate(0,598.58983)">
<text
xml:space="preserve"
style="font-size:17.41555405px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
x="154.5717"
y="351.81107"
id="text3261"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3263"
x="154.5717"
y="351.81107"
style="font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;font-family:Inconsolata;-inkscape-font-specification:Inconsolata Medium">ID: 5449000000996</tspan></text>
</g>
</g>
<g
transform="matrix(0,-1,1,0,261.82237,1622.2884)"
id="g4150">
<text
xml:space="preserve"
style="font-size:8px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Droid Sans;-inkscape-font-specification:Droid Sans"
x="618.97778"
y="320.0275"
id="text3357"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3359"
x="618.97778"
y="320.0275">This is a test sheet intended to help you test the codebar aquisition in OpenERP's Point of Sale module. </tspan><tspan
sodipodi:role="line"
x="618.97778"
y="330.0275"
id="tspan3363">The codes provided in this list are randomly chosen and are encoded in the EAN13 format. Their codes</tspan><tspan
sodipodi:role="line"
x="618.97778"
y="340.0275"
id="tspan3367">and prefixes are not intended to match any specified standard. </tspan></text>
<text
sodipodi:linespacing="125%"
id="text3369"
y="349.54001"
x="618.99207"
style="font-size:6.53564215px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#b3b3b3;fill-opacity:1;stroke:none;font-family:Droid Sans;-inkscape-font-specification:Droid Sans"
xml:space="preserve"><tspan
id="tspan3375"
y="349.54001"
x="618.99207"
sodipodi:role="line"
style="fill:#b3b3b3;fill-opacity:1">This document is licensed by OpenERP S.A. under the Creative Commons CC BY 3.0 license htttp://www.creativecommons.org</tspan></text>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 31 KiB

View File

@ -22,6 +22,7 @@
import pdb import pdb
import openerp import openerp
import addons import addons
import openerp.addons.product.product
import time import time
from datetime import datetime from datetime import datetime
@ -59,16 +60,9 @@ class pos_config(osv.osv):
help="Accounting journal used to post sales entries."), help="Accounting journal used to post sales entries."),
'iface_self_checkout' : fields.boolean('Self Checkout Mode', 'iface_self_checkout' : fields.boolean('Self Checkout Mode',
help="Check this if this point of sale should open by default in a self checkout mode. If unchecked, OpenERP uses the normal cashier mode by default."), help="Check this if this point of sale should open by default in a self checkout mode. If unchecked, OpenERP uses the normal cashier mode by default."),
'iface_websql' : fields.boolean('WebSQL (Faster but Chrome Only)',
help="If have more than 200 products, it's highly suggested to use WebSQL "\
"to store the data in the browser, instead of localStore mechanism. "\
"It's more efficient but works on the Chrome browser only."
),
'iface_led' : fields.boolean('Help Notification'),
'iface_cashdrawer' : fields.boolean('Cashdrawer Interface'), 'iface_cashdrawer' : fields.boolean('Cashdrawer Interface'),
'iface_payment_terminal' : fields.boolean('Payment Terminal Interface'), 'iface_payment_terminal' : fields.boolean('Payment Terminal Interface'),
'iface_electronic_scale' : fields.boolean('Electronic Scale Interface'), 'iface_electronic_scale' : fields.boolean('Electronic Scale Interface'),
'iface_barscan' : fields.boolean('BarScan Interface'),
'iface_vkeyboard' : fields.boolean('Virtual KeyBoard Interface'), 'iface_vkeyboard' : fields.boolean('Virtual KeyBoard Interface'),
'iface_print_via_proxy' : fields.boolean('Print via Proxy'), 'iface_print_via_proxy' : fields.boolean('Print via Proxy'),
@ -77,7 +71,7 @@ class pos_config(osv.osv):
help="This sequence is automatically created by OpenERP but you can change it "\ help="This sequence is automatically created by OpenERP but you can change it "\
"to customize the reference numbers of your orders."), "to customize the reference numbers of your orders."),
'session_ids': fields.one2many('pos.session', 'config_id', 'Sessions'), 'session_ids': fields.one2many('pos.session', 'config_id', 'Sessions'),
'group_by' : fields.boolean('Group By', help="Check this if you want to group the Journal Items by Product while closing a Session"), 'group_by' : fields.boolean('Group Journal Items', help="Check this if you want to group the Journal Items by Product while closing a Session"),
} }
def name_get(self, cr, uid, ids, context=None): def name_get(self, cr, uid, ids, context=None):
@ -93,7 +87,7 @@ class pos_config(osv.osv):
result.append((record.id, record.name+' ('+_('not used')+')')) result.append((record.id, record.name+' ('+_('not used')+')'))
continue continue
session = record.session_ids[0] session = record.session_ids[0]
result.append((record.id, record.name + ' ('+session.user_id.name+', '+states[session.state]+')')) result.append((record.id, record.name + ' ('+session.user_id.name+')')) #, '+states[session.state]+')'))
return result return result
@ -143,8 +137,6 @@ class pos_config(osv.osv):
obj.sequence_id.unlink() obj.sequence_id.unlink()
return super(pos_config, self).unlink(cr, uid, ids, context=context) return super(pos_config, self).unlink(cr, uid, ids, context=context)
pos_config()
class pos_session(osv.osv): class pos_session(osv.osv):
_name = 'pos.session' _name = 'pos.session'
_order = 'id desc' _order = 'id desc'
@ -459,8 +451,6 @@ class pos_session(osv.osv):
'context' : context, 'context' : context,
} }
pos_session()
class pos_order(osv.osv): class pos_order(osv.osv):
_name = "pos.order" _name = "pos.order"
_description = "Point of Sale" _description = "Point of Sale"
@ -1079,8 +1069,6 @@ class pos_order(osv.osv):
self.create_account_move(cr, uid, ids, context=context) self.create_account_move(cr, uid, ids, context=context)
return True return True
pos_order()
class account_bank_statement(osv.osv): class account_bank_statement(osv.osv):
_inherit = 'account.bank.statement' _inherit = 'account.bank.statement'
_columns= { _columns= {
@ -1179,8 +1167,6 @@ class pos_order_line(osv.osv):
}) })
return super(pos_order_line, self).copy_data(cr, uid, id, default, context=context) return super(pos_order_line, self).copy_data(cr, uid, id, default, context=context)
pos_order_line()
class pos_category(osv.osv): class pos_category(osv.osv):
_name = 'pos.category' _name = 'pos.category'
_description = "Point of Sale Category" _description = "Point of Sale Category"
@ -1230,6 +1216,11 @@ class pos_category(osv.osv):
'parent_id': fields.many2one('pos.category','Parent Category', select=True), 'parent_id': fields.many2one('pos.category','Parent Category', select=True),
'child_id': fields.one2many('pos.category', 'parent_id', string='Children Categories'), 'child_id': fields.one2many('pos.category', 'parent_id', string='Children Categories'),
'sequence': fields.integer('Sequence', help="Gives the sequence order when displaying a list of product categories."), 'sequence': fields.integer('Sequence', help="Gives the sequence order when displaying a list of product categories."),
# NOTE: there is no 'default image', because by default we don't show thumbnails for categories. However if we have a thumbnail
# for at least one category, then we display a default image on the other, so that the buttons have consistent styling.
# In this case, the default image is set by the js code.
'image': fields.binary("Image", 'image': fields.binary("Image",
help="This field holds the image used for the category. "\ help="This field holds the image used for the category. "\
"The image is base64 encoded, and PIL-supported. "\ "The image is base64 encoded, and PIL-supported. "\
@ -1252,21 +1243,40 @@ class pos_category(osv.osv):
"Use this field anywhere a small image is required."), "Use this field anywhere a small image is required."),
} }
def _get_default_image(self, cr, uid, context=None):
image_path = openerp.modules.get_module_resource('point_of_sale', 'images', 'default_category_photo.png')
return tools.image_resize_image_big(open(image_path, 'rb').read().encode('base64'))
_defaults = {
'image': _get_default_image,
}
pos_category()
import io, StringIO import io, StringIO
class ean_wizard(osv.osv_memory):
_name = 'pos.ean_wizard'
_columns = {
'ean13_pattern': fields.char('Ean13 Pattern', size=32, required=True, translate=True),
}
def sanitize_ean13(self, cr, uid, ids, context):
for r in self.browse(cr,uid,ids):
ean13 = openerp.addons.product.product.sanitize_ean13(r.ean13_pattern)
m = context.get('active_model')
m_id = context.get('active_id')
self.pool.get(m).write(cr,uid,[m_id],{'ean13':ean13})
return { 'type' : 'ir.actions.act_window_close' }
class product_product(osv.osv): class product_product(osv.osv):
_inherit = 'product.product' _inherit = 'product.product'
#def _get_small_image(self, cr, uid, ids, prop, unknow_none, context=None):
# result = {}
# for obj in self.browse(cr, uid, ids, context=context):
# if not obj.product_image:
# result[obj.id] = False
# continue
# image_stream = io.BytesIO(obj.product_image.decode('base64'))
# img = Image.open(image_stream)
# img.thumbnail((120, 100), Image.ANTIALIAS)
# img_stream = StringIO.StringIO()
# img.save(img_stream, "JPEG")
# result[obj.id] = img_stream.getvalue().encode('base64')
# return result
_columns = { _columns = {
'income_pdt': fields.boolean('Point of Sale Cash In', help="This is a product you can use to put cash into a statement for the point of sale backend."), 'income_pdt': fields.boolean('Point of Sale Cash In', help="This is a product you can use to put cash into a statement for the point of sale backend."),
'expense_pdt': fields.boolean('Point of Sale Cash Out', help="This is a product you can use to take cash from a statement for the point of sale backend, exemple: money lost, transfer to bank, etc."), 'expense_pdt': fields.boolean('Point of Sale Cash Out', help="This is a product you can use to take cash from a statement for the point of sale backend, exemple: money lost, transfer to bank, etc."),
@ -1278,7 +1288,16 @@ class product_product(osv.osv):
'to_weight' : False, 'to_weight' : False,
} }
product_product() def edit_ean(self, cr, uid, ids, context):
return {
'name': "Edit Ean",
'type': 'ir.actions.act_window',
'view_type': 'form',
'view_mode': 'form',
'res_model': 'pos.ean_wizard',
'target' : 'new',
'view_id': False,
'context':context,
}
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -1,12 +1,40 @@
<?xml version="1.0"?> <?xml version="1.0"?>
<openerp> <openerp>
<data> <data>
<record id="base.user_root" model="res.users">
<field name="ean13">0410100000006</field>
</record>
<record id="base.user_demo" model="res.users"> <record id="base.user_demo" model="res.users">
<field name="groups_id" eval="[(4,ref('group_pos_user'))]"/> <field name="groups_id" eval="[(4,ref('group_pos_user'))]"/>
<field name="ean13">0410200000005</field>
</record> </record>
<record id="account.cash_journal" model="account.journal"> <record id="account.cash_journal" model="account.journal">
<field eval="True" name="journal_user"/> <field eval="True" name="journal_user"/>
</record> </record>
<record id="base.user_jsmith" model="res.users">
<field name="name">John Smith (Cashier)</field>
<field name="login">jsmith</field>
<field name="ean13">0410300000004</field>
<field name="groups_id" eval="[(4,ref('group_pos_manager'))]"/>
</record>
<record id="base.user_jdoe" model="res.users">
<field name="name">John Doe (Cashier)</field>
<field name="login">jdoe</field>
<field name="ean13">0410400000003</field>
<field name="groups_id" eval="[(4,ref('group_pos_manager'))]"/>
</record>
<record id="base.user_jbloggs" model="res.users">
<field name="name">Joe Bloggs (Client)</field>
<field name="login">jbloggs</field>
<field name="ean13">0420100000005</field>
<field name="groups_id" eval="[(4,ref('group_pos_user'))]"/>
</record>
<record id="base.user_bsoap" model="res.users">
<field name="name">Bob Soap (Client)</field>
<field name="login">bsoap</field>
<field name="ean13">0420200000004</field>
<field name="groups_id" eval="[(4,ref('group_pos_user'))]"/>
</record>
<!-- Resource: pos.category --> <!-- Resource: pos.category -->
@ -154,6 +182,7 @@
<field name="list_price">1.98</field> <field name="list_price">1.98</field>
<field name="name">Boni Oranges</field> <field name="name">Boni Oranges</field>
<field name="to_weight">True</field> <field name="to_weight">True</field>
<field name="ean13">2100002000003</field>
<field name="pos_categ_id" ref="oranges"/> <field name="pos_categ_id" ref="oranges"/>
<field name="uom_id" ref="product.product_uom_kgm" /> <field name="uom_id" ref="product.product_uom_kgm" />
<field name="uom_po_id" ref="product.product_uom_kgm" /> <field name="uom_po_id" ref="product.product_uom_kgm" />
@ -248,6 +277,7 @@
<field name="list_price">5.10</field> <field name="list_price">5.10</field>
<field name="name">Fishing</field> <field name="name">Fishing</field>
<field name="to_weight">True</field> <field name="to_weight">True</field>
<field name="ean13">2300001000008</field>
<field name="pos_categ_id" ref="rouges_noyau_fruits"/> <field name="pos_categ_id" ref="rouges_noyau_fruits"/>
<field name="uom_id" ref="product.product_uom_kgm" /> <field name="uom_id" ref="product.product_uom_kgm" />
<field name="uom_po_id" ref="product.product_uom_kgm" /> <field name="uom_po_id" ref="product.product_uom_kgm" />
@ -316,6 +346,7 @@
<field name="list_price">1.28</field> <field name="list_price">1.28</field>
<field name="name">Onions</field> <field name="name">Onions</field>
<field name="to_weight">True</field> <field name="to_weight">True</field>
<field name="ean13">2100001000004</field>
<field name="pos_categ_id" ref="oignons_ail_echalotes"/> <field name="pos_categ_id" ref="oignons_ail_echalotes"/>
<field name="uom_id" ref="product.product_uom_kgm" /> <field name="uom_id" ref="product.product_uom_kgm" />
<field name="uom_po_id" ref="product.product_uom_kgm" /> <field name="uom_po_id" ref="product.product_uom_kgm" />
@ -389,6 +420,7 @@
<record id="coca_regular_33cl" model="product.product"> <record id="coca_regular_33cl" model="product.product">
<field name="list_price">0.51</field> <field name="list_price">0.51</field>
<field name="name">Coca-Cola Regular 33cl</field> <field name="name">Coca-Cola Regular 33cl</field>
<field name="ean13">5449000000996</field>
<field name="pos_categ_id" ref="coke"/> <field name="pos_categ_id" ref="coke"/>
<field name="image">/9j/4AAQSkZJRgABAAEBLAEsAAD/2wCEAAoHBwgHBgoICAgLCgoLDhgQDg0NDh0VFhEYIx8lJCIfIiEmKzcvJik0KSEiMEExNDk7Pj4+JS5ESUM8SDc9PjsBCwoKDg0OHBYWHCgoKCgoOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O//AABEIAGQAOAMBEQACEQEDEQH/xAAfAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgv/xAC1EAACAQMDAgQDBQUEBAAAAX0BAgMABBEFEiExQQYTUWEHInEUMoGRoQgjQrHBFVLR8CQzYnKCCQoWFxgZGiUmJygpKjQ1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4eLj5OXm5+jp6vHy8/T19vf4+fr/xAAfAQADAQEBAQEBAQEBAAAAAAAAAQIDBAUGBwgJCgv/xAC1EQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2dri4+Tl5ufo6ery8/T19vf4+fr/2gAMAwEAAhEDEQA/APZSQoJJwByTQBCt1HIxVOSOvtQBBe36WkDyySLGqAse5wKB2M1fEqHDYkZCu7ds4xQFi/Zaql7brPCVdGzjBweDjvQFix9ujVgsmULdM9/oelAiwrB1DKcgjINAFTVL6DT9PluLggIqnI9amUlFXZrQoyrTUY7s5OLWpJbINIs1ozjO6ICRfy4IrGOIT3R3VsslGTUJJ/g/8jPkngaXzZ9Wif2nDrn8wRWqq031OZ4LEL7D+Wo0ahp6uP8AiYaf5f8Ac81cflt/pT9pDuifqtf+SX3MVby2kuPMg1aBPRYSzlfYYGKTq0+5SwWJ/kf3WNy4muLTTUkJkvGlQspYCNcYHXqT1/8A1UqlTlV0h4fC+1m1J2szY8NanHquiQTLtWRB5cqD+Fhwf8fxopT543FjsK8PWcenT0OU8dXUlx4gtNK8wJF5aytlsbiWI/p+PHpXPipNySPZyWlGGHnVtd3t+A6bakaqvQDArJhC7d2Ub1AkJ3gcrke4qZaG9F3locBfMr3blVGAalI+joxtBHVeFirWwIUD6CnHc8fMk1I76JkudJj3n/Uttb6dv6V2RtKHofNyTp13bqZWgXMVp4xbT7NgIZomeRAcjI6GppSSqWR242nKpg/aT3TsjG+IJ2+NLY562i/+hNWeJ+M7sj1wUv8AF+iOTGq3trd7VuJDGH5TOQRmsEe08NTnDZXsdHrtzLcaLbm2eJmMzCBVJHmxMMgjnOQwYEZJycdxW9SKcVY8bBJQry5r7a+TWmvk1Zp9vmZFjoNlqGhwXEt6LK7mkdUeUZicg8KT/C1KMFKCd7M66+ZVqOIlFR5opLbdefmixPpuqeH7Ge4VZ4kDKoUhXVSe+7HzAjoeOeo6ZU6TimzOljKOMqRi2np5p/d0f9INK1u9udL1qKe6dybVWUdMfOAcY/3qVOXuy9C8ThKcK1Fxj9r9H/kaPw+H/FXRe1q9PC/GYZ4/9jf+JFj4h/8AI52h/wCnVf8A0JqrE/GjPIv9yn/i/RHGSQNPfCNMbnfaM+5rnWrse4pqFO76I39X1TSdL1A6GbQ3OnQoI5+fmEo6uno3r64ronOMXy20PHw2GxFel7fm5Zt3Xa3Z9127Fy2tZlsprvSpIdas3wZrdx88o/2l7SD+8OuOmetxi7Nx1X9ficdWrH2ihVTpyWz6L0f8r7dPTavqlzYf8Ii8dn/aUQFwgNtOTtgP905/h7jryO1TUcfZO1zbCU6v11OfI/deq6+fr38jn9PJR7he0tuy/lhv/Za5oPf0PZrK6i+0l/l+p1Xw7GfFg9rVv5itsL8Z5We/7p/28S/Ec7PF1m3/AE7L/wChNV4n40TkCvhJr+9+hyDTJFeq8jFVWQEkdRzXNH4j2nFyptLsXte0uOXVJwkPkSPKQGVyyFzyA2c7d2chuhyOnONqsLs4MFipRoxu7pL52XVd7dVuvuvL4UiSVEax2LqFqWeSA5WS4XqArZGPQjp3welOgrrTdGGaScW1P4JWs+i9VZ+qf5Brl/4hutFf7S9wLRZQk0U8IV0bquWAG4e/HPXtkqSqOOuw8DQwdOuuVLmtdNO6a66X0fkZNm25tw/55t/6Ca51uepU0XzX5nWfDZt/it/a1b+a1vhfj+R5Gfq2FX+L/MsfEqPzPFOnrnG63A/8eNaYle+jPh+XLhaj8/0PPrknz5Of4j/Oudn0kPhXodLpUyeIrKKx+1my1i2TZbXAcqJ0/uMR3Hatov2itezPExcHg6jny81OTu1vZ90NstMaCJbGY2+n6rBIzYu0AW4B6bZOoI6cGiELK2zM6+JU5c6vODX2Xt6r/NEGq6vq2paNKj3Mnk28qpc28gBKNztO7GSuR3OQcdampOco7muEwuHo101FXabT799Nr+n4GXo5LX8cQ/jyv5g1lBXaPSxNlSb7HW/C47vFEv8A16v/AOhLW2G+P5HlcR6YVf4v0ZpfEhf+Km0pvWIj/wAerTEfGjkyF/7NV9Tzi5/4+JM/3j/OuZn1EPhXoafhq1mu5b6KGW3jLWpXNwPlyWULhv4WyQQfUVdKLd/Q87M6saag2m/e6b7O+nVW3RuJrerafbNZ+INMj1JIRl0mH7xF/vZIIYe4/E1sqk0rSVzyZYPD1Z8+HqOF+23p3T8vuMzVvEdpe6VJp+kaQllA7LJOwxlsHgce+KzqVU42irHbg8vqU6yqVajk9kUfC8XneI7KPrmT+hqKKvNHdmMuXCzfkdP8K+fFEx7C0b/0JK0w3x/I83iP/do/4v0Zp/EzP9u6QV67G7e4q8T8SOTh+3sK1/L9Tzi9QR3cqhg2G6iuaW59PSleCZo+GEuPt8skLwCOOL9+lxnZIhIBBwOnIOe2M1VK/McGaSh7NJp3b0tunvp/l12Oy0W4t5fM+yalA1rEGVrS+Ic2zYI+V8/Mvb0wetddNp7PTz6HzuMhONueD5nb3o6c3quj/U5e401IfAQvFaJXmvizYP3lG5QF9ccn8a55w/dX8z2KWIcsx5NbKH52eongGEzeLrQ44jDuf++SP5kVOGV6iNs7ny4Kfnb8zc+FA/4qS79rVv8A0Na0wy/efI5OJH/s8P8AF+jND4mNjxBpABx8jc/jV4j4kc+QL9xV+R59er5upugO3e4GW7Z71yvVn0dJ8tFPyNa10uXStUeay1FWktUdnjERMnysEZWjz05z16c1pGDjK6Z5lbExxFFKcNJNa301V00/61NaaDwta3gv5RcWnygSwxLuhcsuSmMZXr0OOlbctNO+x50amPqQ5FaXZvRqz38/VXOYvLoz+HrKGMkRW9xMAp9DtIz+ZrCbvBep69Cny4mbe7jH9U/0Oj+GNsDf318w4ghCA+7HP/staYRe82cHEdT91CHd/l/w5rfC3TZba/kvJAdtzbNs9MB1zVYaOt/Iw4hxMZpQX2WvyY74lRyHxLpUgU7PKOGxnkNk/pijEfGgyGUfq1VeZ57qY238w56965pbn0lB3pI6W1jm1+KLWNIl8vXLJQJ4sj9+AMBxnjJHBHf+eyTn70d0eHWccJJ0aqvSls+3l/kEGseG2upptV0y4srxlZLiGIHy5SeuVPQ559jzmqjOm3qrEVMJjVFKlNSj0b3XzOdtoTNpGpOisIYXikBPY5Kgfkx/KsbXiz1JS5a9NPdpr8L/AKHoHhi0/sXwBc3kg2yTwyTn6bcKP6/jXVQjyUmz57Mav1nMowWyaX46mv4Jkid7ZIoyPL06Pe/GCSE/wP5GrpWtH0OLMIy9rVb61P8AP/gHTappNnrFqba8i3qDlWBwyH1B7GrnBSVmcmHxFShLmgzzTxJ8OprWRri3vg6N084Yx9SOK5pYZ9GfQ4XiNRjacPu/yf8Amc1BpuoaTdJdW9zGssZyro+B/wCPYzUKjUi7o6amc4HEQcZ3s+6/yN3/AISa11CMJ4g0S2uZFGPNhljyfwzkfnW2sviieW1Sou9DEWXZ3/y/QjnuLXVUh0uysU0zS/NEk77wWkx9M/5xSlTcvdSsi6eMp0G6sqvPO1lo7I7prBPEejvp8BNvauFQyDBwoI4XqO2K3lFSjY8nD4mVGsqlrtfmbOi6HYaDZi1sItinlmJyzn1JpU6cYKyFisXVxM+abuaB6mrZziEBhggEHsaAM668PaPekm406Bye+3H8qVkO7M5vAPhlm3HTh9PMb/GiwcxetPDGh2JBt9MgUjoSu4/rmiyC7NQAKAFAAHQCmIUUIAAA/9k=</field> <field name="image">/9j/4AAQSkZJRgABAAEBLAEsAAD/2wCEAAoHBwgHBgoICAgLCgoLDhgQDg0NDh0VFhEYIx8lJCIfIiEmKzcvJik0KSEiMEExNDk7Pj4+JS5ESUM8SDc9PjsBCwoKDg0OHBYWHCgoKCgoOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O//AABEIAGQAOAMBEQACEQEDEQH/xAAfAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgv/xAC1EAACAQMDAgQDBQUEBAAAAX0BAgMABBEFEiExQQYTUWEHInEUMoGRoQgjQrHBFVLR8CQzYnKCCQoWFxgZGiUmJygpKjQ1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4eLj5OXm5+jp6vHy8/T19vf4+fr/xAAfAQADAQEBAQEBAQEBAAAAAAAAAQIDBAUGBwgJCgv/xAC1EQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2dri4+Tl5ufo6ery8/T19vf4+fr/2gAMAwEAAhEDEQA/APZSQoJJwByTQBCt1HIxVOSOvtQBBe36WkDyySLGqAse5wKB2M1fEqHDYkZCu7ds4xQFi/Zaql7brPCVdGzjBweDjvQFix9ujVgsmULdM9/oelAiwrB1DKcgjINAFTVL6DT9PluLggIqnI9amUlFXZrQoyrTUY7s5OLWpJbINIs1ozjO6ICRfy4IrGOIT3R3VsslGTUJJ/g/8jPkngaXzZ9Wif2nDrn8wRWqq031OZ4LEL7D+Wo0ahp6uP8AiYaf5f8Ac81cflt/pT9pDuifqtf+SX3MVby2kuPMg1aBPRYSzlfYYGKTq0+5SwWJ/kf3WNy4muLTTUkJkvGlQspYCNcYHXqT1/8A1UqlTlV0h4fC+1m1J2szY8NanHquiQTLtWRB5cqD+Fhwf8fxopT543FjsK8PWcenT0OU8dXUlx4gtNK8wJF5aytlsbiWI/p+PHpXPipNySPZyWlGGHnVtd3t+A6bakaqvQDArJhC7d2Ub1AkJ3gcrke4qZaG9F3locBfMr3blVGAalI+joxtBHVeFirWwIUD6CnHc8fMk1I76JkudJj3n/Uttb6dv6V2RtKHofNyTp13bqZWgXMVp4xbT7NgIZomeRAcjI6GppSSqWR242nKpg/aT3TsjG+IJ2+NLY562i/+hNWeJ+M7sj1wUv8AF+iOTGq3trd7VuJDGH5TOQRmsEe08NTnDZXsdHrtzLcaLbm2eJmMzCBVJHmxMMgjnOQwYEZJycdxW9SKcVY8bBJQry5r7a+TWmvk1Zp9vmZFjoNlqGhwXEt6LK7mkdUeUZicg8KT/C1KMFKCd7M66+ZVqOIlFR5opLbdefmixPpuqeH7Ge4VZ4kDKoUhXVSe+7HzAjoeOeo6ZU6TimzOljKOMqRi2np5p/d0f9INK1u9udL1qKe6dybVWUdMfOAcY/3qVOXuy9C8ThKcK1Fxj9r9H/kaPw+H/FXRe1q9PC/GYZ4/9jf+JFj4h/8AI52h/wCnVf8A0JqrE/GjPIv9yn/i/RHGSQNPfCNMbnfaM+5rnWrse4pqFO76I39X1TSdL1A6GbQ3OnQoI5+fmEo6uno3r64ronOMXy20PHw2GxFel7fm5Zt3Xa3Z9127Fy2tZlsprvSpIdas3wZrdx88o/2l7SD+8OuOmetxi7Nx1X9ficdWrH2ihVTpyWz6L0f8r7dPTavqlzYf8Ii8dn/aUQFwgNtOTtgP905/h7jryO1TUcfZO1zbCU6v11OfI/deq6+fr38jn9PJR7he0tuy/lhv/Za5oPf0PZrK6i+0l/l+p1Xw7GfFg9rVv5itsL8Z5We/7p/28S/Ec7PF1m3/AE7L/wChNV4n40TkCvhJr+9+hyDTJFeq8jFVWQEkdRzXNH4j2nFyptLsXte0uOXVJwkPkSPKQGVyyFzyA2c7d2chuhyOnONqsLs4MFipRoxu7pL52XVd7dVuvuvL4UiSVEax2LqFqWeSA5WS4XqArZGPQjp3welOgrrTdGGaScW1P4JWs+i9VZ+qf5Brl/4hutFf7S9wLRZQk0U8IV0bquWAG4e/HPXtkqSqOOuw8DQwdOuuVLmtdNO6a66X0fkZNm25tw/55t/6Ca51uepU0XzX5nWfDZt/it/a1b+a1vhfj+R5Gfq2FX+L/MsfEqPzPFOnrnG63A/8eNaYle+jPh+XLhaj8/0PPrknz5Of4j/Oudn0kPhXodLpUyeIrKKx+1my1i2TZbXAcqJ0/uMR3Hatov2itezPExcHg6jny81OTu1vZ90NstMaCJbGY2+n6rBIzYu0AW4B6bZOoI6cGiELK2zM6+JU5c6vODX2Xt6r/NEGq6vq2paNKj3Mnk28qpc28gBKNztO7GSuR3OQcdampOco7muEwuHo101FXabT799Nr+n4GXo5LX8cQ/jyv5g1lBXaPSxNlSb7HW/C47vFEv8A16v/AOhLW2G+P5HlcR6YVf4v0ZpfEhf+Km0pvWIj/wAerTEfGjkyF/7NV9Tzi5/4+JM/3j/OuZn1EPhXoafhq1mu5b6KGW3jLWpXNwPlyWULhv4WyQQfUVdKLd/Q87M6saag2m/e6b7O+nVW3RuJrerafbNZ+INMj1JIRl0mH7xF/vZIIYe4/E1sqk0rSVzyZYPD1Z8+HqOF+23p3T8vuMzVvEdpe6VJp+kaQllA7LJOwxlsHgce+KzqVU42irHbg8vqU6yqVajk9kUfC8XneI7KPrmT+hqKKvNHdmMuXCzfkdP8K+fFEx7C0b/0JK0w3x/I83iP/do/4v0Zp/EzP9u6QV67G7e4q8T8SOTh+3sK1/L9Tzi9QR3cqhg2G6iuaW59PSleCZo+GEuPt8skLwCOOL9+lxnZIhIBBwOnIOe2M1VK/McGaSh7NJp3b0tunvp/l12Oy0W4t5fM+yalA1rEGVrS+Ic2zYI+V8/Mvb0wetddNp7PTz6HzuMhONueD5nb3o6c3quj/U5e401IfAQvFaJXmvizYP3lG5QF9ccn8a55w/dX8z2KWIcsx5NbKH52eongGEzeLrQ44jDuf++SP5kVOGV6iNs7ny4Kfnb8zc+FA/4qS79rVv8A0Na0wy/efI5OJH/s8P8AF+jND4mNjxBpABx8jc/jV4j4kc+QL9xV+R59er5upugO3e4GW7Z71yvVn0dJ8tFPyNa10uXStUeay1FWktUdnjERMnysEZWjz05z16c1pGDjK6Z5lbExxFFKcNJNa301V00/61NaaDwta3gv5RcWnygSwxLuhcsuSmMZXr0OOlbctNO+x50amPqQ5FaXZvRqz38/VXOYvLoz+HrKGMkRW9xMAp9DtIz+ZrCbvBep69Cny4mbe7jH9U/0Oj+GNsDf318w4ghCA+7HP/staYRe82cHEdT91CHd/l/w5rfC3TZba/kvJAdtzbNs9MB1zVYaOt/Iw4hxMZpQX2WvyY74lRyHxLpUgU7PKOGxnkNk/pijEfGgyGUfq1VeZ57qY238w56965pbn0lB3pI6W1jm1+KLWNIl8vXLJQJ4sj9+AMBxnjJHBHf+eyTn70d0eHWccJJ0aqvSls+3l/kEGseG2upptV0y4srxlZLiGIHy5SeuVPQ559jzmqjOm3qrEVMJjVFKlNSj0b3XzOdtoTNpGpOisIYXikBPY5Kgfkx/KsbXiz1JS5a9NPdpr8L/AKHoHhi0/sXwBc3kg2yTwyTn6bcKP6/jXVQjyUmz57Mav1nMowWyaX46mv4Jkid7ZIoyPL06Pe/GCSE/wP5GrpWtH0OLMIy9rVb61P8AP/gHTappNnrFqba8i3qDlWBwyH1B7GrnBSVmcmHxFShLmgzzTxJ8OprWRri3vg6N084Yx9SOK5pYZ9GfQ4XiNRjacPu/yf8Amc1BpuoaTdJdW9zGssZyro+B/wCPYzUKjUi7o6amc4HEQcZ3s+6/yN3/AISa11CMJ4g0S2uZFGPNhljyfwzkfnW2sviieW1Sou9DEWXZ3/y/QjnuLXVUh0uysU0zS/NEk77wWkx9M/5xSlTcvdSsi6eMp0G6sqvPO1lo7I7prBPEejvp8BNvauFQyDBwoI4XqO2K3lFSjY8nD4mVGsqlrtfmbOi6HYaDZi1sItinlmJyzn1JpU6cYKyFisXVxM+abuaB6mrZziEBhggEHsaAM668PaPekm406Bye+3H8qVkO7M5vAPhlm3HTh9PMb/GiwcxetPDGh2JBt9MgUjoSu4/rmiyC7NQAKAFAAHQCmIUUIAAA/9k=</field>
</record> </record>
@ -1022,3 +1054,4 @@
</data> </data>
</openerp> </openerp>

View File

@ -55,10 +55,14 @@
</group> </group>
</form> </form>
</field> </field>
<group class="oe_subtotal_footer"> <group class="oe_subtotal_footer oe_right" colspan="2" name="order_total">
<field name="amount_tax"/> <field name="amount_tax"/>
<field name="amount_total"/> <div class="oe_subtotal_footer_separator oe_inline">
<button name="button_dummy" string="Update" class="oe_link" states="draft"/> <label for="amount_total" />
<button name="button_dummy"
states="draft" string="(update)" type="object" class="oe_edit_only oe_link"/>
</div>
<field name="amount_total" nolabel="1" class="oe_subtotal_footer_separator"/>
</group> </group>
</page> </page>
<page string="Payments"> <page string="Payments">
@ -630,6 +634,9 @@
<field name="expense_pdt"/> <field name="expense_pdt"/>
</group> </group>
</group> </group>
<field name="ean13" position="after">
<button name="edit_ean" type="object" string="Edit" />
</field>
</field> </field>
</record> </record>
@ -773,10 +780,10 @@
<sheet> <sheet>
<group> <group>
<field name="name"/> <field name="name"/>
<field name="shop_id" widget="selection" /> <field name="shop_id" widget="selection" groups="stock.group_locations" />
<field name="journal_id" widget="selection" /> <field name="journal_id" widget="selection" />
<field name="sequence_id" readonly="1"/> <field name="sequence_id" readonly="1" groups="base.group_no_one" />
<field name="group_by" /> <field name="group_by" groups="account.group_account_user" />
</group> </group>
<separator string="Available Payment Methods" colspan="4"/> <separator string="Available Payment Methods" colspan="4"/>
<field name="journal_ids" colspan="4" nolabel="1"> <field name="journal_ids" colspan="4" nolabel="1">
@ -791,14 +798,11 @@
<group string="Material Interfaces" > <group string="Material Interfaces" >
<group> <group>
<field name="iface_self_checkout" /> <field name="iface_self_checkout" />
<field name="iface_websql" />
<field name="iface_led" />
<field name="iface_cashdrawer" /> <field name="iface_cashdrawer" />
<field name="iface_payment_terminal" /> <field name="iface_payment_terminal" />
</group> </group>
<group> <group>
<field name="iface_electronic_scale" /> <field name="iface_electronic_scale" />
<field name="iface_barscan" />
<field name="iface_vkeyboard" /> <field name="iface_vkeyboard" />
<field name="iface_print_via_proxy" /> <field name="iface_print_via_proxy" />
</group> </group>
@ -877,7 +881,7 @@
class="oe_highlight"/> class="oe_highlight"/>
<button name="close" type="workflow" string="Validate Closing &amp; Post Entries" states="closing_control" <button name="close" type="workflow" string="Validate Closing &amp; Post Entries" states="closing_control"
class="oe_highlight"/> class="oe_highlight"/>
<button name="open_frontend_cb" type="object" string="Start Selling" states="opened"/> <button name="open_frontend_cb" type="object" string="Continue Selling" states="opened"/>
<div class="oe_right"> <div class="oe_right">
<field name="state" widget="statusbar" statusbar_visible="opening_control,opened,closing_control,closed" nolabel="1"/> <field name="state" widget="statusbar" statusbar_visible="opening_control,opened,closing_control,closed" nolabel="1"/>
</div> </div>
@ -888,7 +892,7 @@
<button name="%(action_pos_box_out)d" string="Take Money Out" type="action" states="opened,closing_control"/> <button name="%(action_pos_box_out)d" string="Take Money Out" type="action" states="opened,closing_control"/>
</div> </div>
<h1 class="oe_title"> <h1 class="oe_title">
Point of Sale Session: Session:
<field name="name" attrs="{'invisible': [('name','=','/')]}" class="oe_inline"/> <field name="name" attrs="{'invisible': [('name','=','/')]}" class="oe_inline"/>
</h1> </h1>
<field name="has_opening_control" invisible="1" /> <field name="has_opening_control" invisible="1" />
@ -1057,5 +1061,20 @@
parent="menu_point_of_sale" parent="menu_point_of_sale"
id="menu_pos_session_opening" sequence="0"/> id="menu_pos_session_opening" sequence="0"/>
<record model="ir.ui.view" id="pos_ean13_generator">
<field name="name">pos.ean_wizard</field>
<field name="model">pos.ean_wizard</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Ean13 Generator" version="7.0">
<field name="ean13_pattern"/>
<footer>
<button name="sanitize_ean13" type="object" string="Apply"/>
</footer>
</form>
</field>
</record>
</data> </data>
</openerp> </openerp>

View File

@ -37,14 +37,14 @@ class pos_sales_user(report_sxw.rml_parse):
dt1 = form['date_start'] + ' 00:00:00' dt1 = form['date_start'] + ' 00:00:00'
dt2 = form['date_end'] + ' 23:59:59' dt2 = form['date_end'] + ' 23:59:59'
data={} data={}
self.cr.execute("select po.name as pos,po.date_order,ru.name as user,po.state,rc.name " \ self.cr.execute("select po.name as pos,po.date_order,rp.name as user,po.state,rc.name " \
"from pos_order as po,res_users as ru,res_company as rc " \ "from pos_order as po,res_users as ru,res_company as rc, res_partner as rp " \
"where po.date_order >= %s and po.date_order <= %s " \ "where po.date_order >= %s and po.date_order <= %s " \
"and po.company_id=rc.id and po.user_id=ru.id and po.user_id IN %s " \ "and po.company_id=rc.id and po.user_id=ru.id and po.user_id IN %s and ru.partner_id = rp.id" \
,(dt1,dt2,tuple(form['user_id']))) ,(dt1,dt2,tuple(form['user_id'])))
return self.cr.dictfetchall() return self.cr.dictfetchall()
report_sxw.report_sxw('report.pos.sales.user', 'pos.order', 'addons/point_of_sale/report/pos_sales_user.rml', parser=pos_sales_user,header='internal') report_sxw.report_sxw('report.pos.sales.user', 'pos.order', 'addons/point_of_sale/report/pos_sales_user.rml', parser=pos_sales_user,header='internal')
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -1,38 +1,8 @@
#!/usr/bin/env python #!/usr/bin/env python
from osv import osv, fields from osv import osv, fields
import math import math
import openerp.addons.product.product
def is_pair(x):
return not x%2
# This code is a duplicate of product#check_ean function
def check_ean(eancode):
if not eancode:
return True
if len(eancode) <> 13:
return False
try:
int(eancode)
except:
return False
oddsum=0
evensum=0
total=0
eanvalue=eancode
reversevalue = eanvalue[::-1]
finalean=reversevalue[1:]
for i in range(len(finalean)):
if is_pair(i):
oddsum += int(finalean[i])
else:
evensum += int(finalean[i])
total=(oddsum * 3) + evensum
check = int(10 - math.ceil(total % 10.0)) %10
if check != int(eancode[-1]):
return False
return True
class res_users(osv.osv): class res_users(osv.osv):
_inherit = 'res.users' _inherit = 'res.users'
@ -43,10 +13,22 @@ class res_users(osv.osv):
def _check_ean(self, cr, uid, ids, context=None): def _check_ean(self, cr, uid, ids, context=None):
return all( return all(
check_ean(user.ean13) == True openerp.addons.product.product.check_ean(user.ean13) == True
for user in self.browse(cr, uid, ids, context=context) for user in self.browse(cr, uid, ids, context=context)
) )
def edit_ean(self, cr, uid, ids, context):
return {
'name': "Edit Ean",
'type': 'ir.actions.act_window',
'view_type': 'form',
'view_mode': 'form',
'res_model': 'pos.ean_wizard',
'target' : 'new',
'view_id': False,
'context':context,
}
_constraints = [ _constraints = [
(_check_ean, "Error: Invalid ean code", ['ean13'],), (_check_ean, "Error: Invalid ean code", ['ean13'],),
] ]

View File

@ -20,8 +20,11 @@
<field name="arch" type="xml"> <field name="arch" type="xml">
<notebook position="inside"> <notebook position="inside">
<page string="Point Of Sale"> <page string="Point Of Sale">
<field name="ean13" /> <group>
<field name="pos_config" /> <field name="pos_config" />
<field name="ean13" />
<button name="edit_ean" type="object" string="Edit" />
</group>
</page> </page>
</notebook> </notebook>
</field> </field>

View File

@ -60,7 +60,8 @@ access_product_uom_manager,product.uom manager,product.model_product_uom,group_p
access_res_partner_manager,res.partner manager,base.model_res_partner,group_pos_manager,1,0,0,0 access_res_partner_manager,res.partner manager,base.model_res_partner,group_pos_manager,1,0,0,0
access_product_category_manager,product.category manager,product.model_product_category,group_pos_manager,1,1,1,1 access_product_category_manager,product.category manager,product.model_product_category,group_pos_manager,1,1,1,1
access_product_pricelist_manager,product.pricelist manager,product.model_product_pricelist,group_pos_manager,1,0,0,0 access_product_pricelist_manager,product.pricelist manager,product.model_product_pricelist,group_pos_manager,1,0,0,0
access_product_category_pos_manager,pos.category manager,model_pos_category,group_pos_manager,1,1,1,"1""" access_product_category_pos_manager,pos.category manager,model_pos_category,group_pos_manager,1,1,1,1
access_product_category_pos_user,pos.category user,model_pos_category,group_pos_user,1,0,0,"0""" access_product_category_pos_user,pos.category user,model_pos_category,group_pos_user,1,0,0,0
access_pos_session_user,pos.session user,model_pos_session,group_pos_user,1,1,1,0 access_pos_session_user,pos.session user,model_pos_session,group_pos_user,1,1,1,0
access_pos_config_user,pos.config user,model_pos_config,group_pos_user,1,1,1,0 access_pos_config_user,pos.config user,model_pos_config,group_pos_user,1,1,1,0
access_ir_sequence_manager,ir.sequence manager,base.model_ir_sequence,group_pos_manager,1,1,1,1

1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
60 access_res_partner_manager res.partner manager base.model_res_partner group_pos_manager 1 0 0 0
61 access_product_category_manager product.category manager product.model_product_category group_pos_manager 1 1 1 1
62 access_product_pricelist_manager product.pricelist manager product.model_product_pricelist group_pos_manager 1 0 0 0
63 access_product_category_pos_manager pos.category manager model_pos_category group_pos_manager 1 1 1 1" 1
64 access_product_category_pos_user pos.category user model_pos_category group_pos_user 1 0 0 0" 0
65 access_pos_session_user pos.session user model_pos_session group_pos_user 1 1 1 0
66 access_pos_config_user pos.config user model_pos_config group_pos_user 1 1 1 0
67 access_ir_sequence_manager ir.sequence manager base.model_ir_sequence group_pos_manager 1 1 1 1

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -31,6 +31,7 @@
background: -ms-linear-gradient(top,rgba(0,0,0,0.09),rgba(0,0,0,0)); background: -ms-linear-gradient(top,rgba(0,0,0,0.09),rgba(0,0,0,0));
background: linear-gradient(top,rgba(0,0,0,0.09),rgba(0,0,0,0)); background: linear-gradient(top,rgba(0,0,0,0.09),rgba(0,0,0,0));
} }
.point-of-sale .darker-shadow-top{ .point-of-sale .darker-shadow-top{
position: absolute; position: absolute;
top:0; top:0;
@ -42,6 +43,7 @@
background: -ms-linear-gradient(top,rgba(0,0,0,0.15),rgba(0,0,0,0)); background: -ms-linear-gradient(top,rgba(0,0,0,0.15),rgba(0,0,0,0));
background: linear-gradient(top,rgba(0,0,0,0.15),rgba(0,0,0,0)); background: linear-gradient(top,rgba(0,0,0,0.15),rgba(0,0,0,0));
} }
/* ********* The black loading screen ********* */ /* ********* The black loading screen ********* */
.point-of-sale .loader{ .point-of-sale .loader{
@ -156,11 +158,11 @@
} }
.point-of-sale #rightheader button { .point-of-sale #rightheader button {
color: black; color: #273072;
height:29px; height:27px;
margin:2px; margin:3px;
margin-right:0px; margin-right:0px;
border: 1px solid black; border: 1px solid #353A7E;
background: #7f82ac; background: #7f82ac;
background: -webkit-gradient(linear, left top, left bottom, from(#b2b3d7), to(#7f82ac)); background: -webkit-gradient(linear, left top, left bottom, from(#b2b3d7), to(#7f82ac));
background: -moz-linear-gradient(#b2b3d7, #7f82ac); background: -moz-linear-gradient(#b2b3d7, #7f82ac);
@ -187,6 +189,33 @@
font-weight: 900; font-weight: 900;
} }
/* c) The session buttons */
.point-of-sale #rightheader .header-button{
float:right;
height:32px;
padding-left:10px;
padding-right:10px;
border-right: 1px solid #3a3a3a;
border-left: 1px solid #3a3a3a;
color:#DDD;
line-height:32px;
text-align:center;
cursor: pointer;
-webkit-transition-property: background;
-webkit-transition-duration: 0.2s;
-webkit-transition-timing-function: ease-out;
}
.point-of-sale #rightheader .header-button:last-child{
border-left: 1px solid #3a3a3a;
}
.point-of-sale #rightheader .header-button:hover{
background: rgba(0,0,0,0.2);
text-shadow: #000 0px 0px 3px;
color:#EEE;
}
/* c) The notifications indicator */ /* c) The notifications indicator */
.point-of-sale .oe_pos_synch-notification{ .point-of-sale .oe_pos_synch-notification{
@ -234,7 +263,8 @@
left:0; left:0;
width:440px; width:440px;
top:0px; top:0px;
bottom:105px; /*bottom:105px;*/
bottom:0;
border-right: solid 1px #CECBCB; border-right: solid 1px #CECBCB;
background-color: white; background-color: white;
} }
@ -318,7 +348,8 @@
.point-of-sale #rightpane { .point-of-sale #rightpane {
position: absolute; position: absolute;
top: 0; top: 0;
bottom: 105px; /*bottom: 105px;*/
bottom:0;
left: 440px; left: 440px;
right: 0; right: 0;
vertical-align: top; vertical-align: top;
@ -454,6 +485,42 @@
cursor: pointer; cursor: pointer;
} }
.point-of-sale .category-simple-button{
position: relative;
display: inline-block;
font-size: 14px;
margin-right:10px;
padding:5px;
cursor: pointer;
border: 1px solid #cacaca;
border-radius: 4px;
background: #e2e2e2;
background: -webkit-linear-gradient(#f0f0f0, #e2e2e2);
background: -moz-linear-gradient(#f0f0f0, #e2e2e2);
background: -ms-linear-gradient(#f0f0f0, #e2e2e2);
background: linear-gradient(#f0f0f0, #e2e2e2);
-webkit-box-shadow: 0px 2px 2px rgba(0,0,0, 0.1);
-moz-box-shadow: 0px 2px 2px rgba(0,0,0, 0.1);
box-shadow: 0px 2px 2px rgba(0,0,0, 0.1);
}
.point-of-sale .category-simple-button:hover {
color: white;
background: #7f82ac;
border: 1px solid #7f82ac;
background: -webkit-linear-gradient(#9d9fc5, #7f82ac);
background: -moz-linear-gradient(#9d9fc5, #7f82ac);
background: -ms-linear-gradient(#9d9fc5, #7f82ac);
background: linear-gradient(#9d9fc5, #7f82ac);
-webkit-transition-property: background, border;
-webkit-transition-duration: 0.2s;
-webkit-transition-timing-function: ease-out;
}
.point-of-sale .category-button .category-img { .point-of-sale .category-button .category-img {
position: relative; position: relative;
width: 120px; width: 120px;
@ -463,7 +530,8 @@
} }
.point-of-sale .category-button .category-img img { .point-of-sale .category-button .category-img img {
height: 100px; max-height: 100px;
max-width: 120px;
} }
.point-of-sale .category-button .category-name { .point-of-sale .category-button .category-name {
@ -490,6 +558,7 @@
position:relative; position:relative;
vertical-align: top; vertical-align: top;
display: inline-block; display: inline-block;
line-height: 100px;
font-size: 11px; font-size: 11px;
margin: 5px; margin: 5px;
width: 120px; width: 120px;
@ -497,9 +566,9 @@
background:#fff; background:#fff;
border: 1px solid #fff; border: 1px solid #fff;
border-radius: 3px; border-radius: 3px;
-webkit-box-shadow: 0px 1px 8px rgba(0,0,0,0.2); -webkit-box-shadow: 0px 1px 8px rgba(127,130,172,0.4);
-moz-box-shadow: 0px 1px 8px rgba(0,0,0,0.2); -moz-box-shadow: 0px 1px 8px rgba(127,130,172,0.4);
box-shadow: 0px 1px 8px rgba(0,0,0,0.2); box-shadow: 0px 1px 8px rgba(127,130,172,0.4);
} }
.point-of-sale .product .product-img { .point-of-sale .product .product-img {
@ -511,7 +580,8 @@
} }
.point-of-sale .product .product-img img { .point-of-sale .product .product-img img {
height: 100px; max-height: 100px;
max-width: 120px;
} }
.point-of-sale .product .price-tag { .point-of-sale .product .price-tag {
@ -520,6 +590,7 @@
right: 2px; right: 2px;
vertical-align: top; vertical-align: top;
color: white; color: white;
line-height: 14px;
background: #7f82ac; background: #7f82ac;
padding: 2px 5px; padding: 2px 5px;
border-radius: 3px; border-radius: 3px;
@ -533,6 +604,7 @@
box-sizing: border-box; box-sizing: border-box;
bottom:0; bottom:0;
top:auto; top:auto;
line-height: 14px;
width:100%; width:100%;
background: -webkit-linear-gradient(-90deg,rgba(255,255,255,0),rgba(255,255,255,1), rgba(255,255,255,1)); background: -webkit-linear-gradient(-90deg,rgba(255,255,255,0),rgba(255,255,255,1), rgba(255,255,255,1));
background: -moz-linear-gradient(-90deg,rgba(255,255,255,0),rgba(255,255,255,1), rgba(255,255,255,1)); background: -moz-linear-gradient(-90deg,rgba(255,255,255,0),rgba(255,255,255,1), rgba(255,255,255,1));
@ -598,8 +670,16 @@
display: inline-block; display: inline-block;
font-size: 1.5em; font-size: 1.5em;
} }
.point-of-sale .greyed-out{
color: #AAA;
}
.point-of-sale .pos-step-container input{
font-size: 1em;
}
.point-of-sale .pos-payment-container { .point-of-sale .pos-payment-container {
text-align: left; text-align: left;
min-width: 500px;
} }
.point-of-sale .pos-payment-container .left-block{ .point-of-sale .pos-payment-container .left-block{
display: inline-block; display: inline-block;
@ -648,8 +728,17 @@
width: 300px; width: 300px;
background-color: white; background-color: white;
margin: 20px; margin: 20px;
padding: 10px; padding: 15px;
padding-bottom:30px;
display: inline-block; display: inline-block;
font-family: "Inconsolata";
-webkit-box-shadow: 0px 5px 16px rgba(0,0,0, 0.3);
-moz-box-shadow: 0px 5px 16px rgba(0,0,0, 0.3);
box-shadow: 0px 5px 16px rgba(0,0,0, 0.3);
}
.point-of-sale .pos-sale-ticket .emph{
font-size: 20px;
margin:5px;
} }
.point-of-sale .pos-sale-ticket table { .point-of-sale .pos-sale-ticket table {
width: 100%; width: 100%;
@ -660,18 +749,18 @@
} }
@media print { @media print {
#oe_header, #oe_menu, .point-of-sale #topheader, .point-of-sale #leftpane { .point-of-sale #topheader, .point-of-sale #leftpane {
display: none; display: none !important;
} }
.point-of-sale #content { .point-of-sale #content {
top: 0px; top: 0px !important;
} }
.point-of-sale #rightpane { .point-of-sale #rightpane {
left: 0px; left: 0px !important;
background-color: white; background-color: white;
} }
#receipt-screen header { #receipt-screen header {
display: none; display: none !important;
} }
#receipt-screen { #receipt-screen {
text-align: left; text-align: left;
@ -761,6 +850,28 @@
font-family: "Inconsolata"; font-family: "Inconsolata";
} }
/* e) The Welcome Screen */
.point-of-sale .goodbye-message{
position: absolute;
left:50%;
top:30%;
width:500px;
height:400px;
margin-left: -250px;
margin-top: -200px;
padding:10px;
padding-top:20px;
text-align:center;
font-size:20px;
font-weight:bold;
background-color: #F0EEEE;
border: 1px solid #E0DDDD;
-webkit-box-shadow: 0px 10px 20px rgba(0,0,0, 0.3);
-moz-box-shadow: 0px 10px 20px rgba(0,0,0, 0.3);
-ms-box-shadow: 0px 10px 20px rgba(0,0,0, 0.3);
z-index:1150;
}
/* ********* The OrderWidget ********* */ /* ********* The OrderWidget ********* */
.point-of-sale .order-container{ .point-of-sale .order-container{
@ -846,6 +957,10 @@
-moz-transition: background 50ms ease-in-out; -moz-transition: background 50ms ease-in-out;
transition: background 50ms ease-in-out; transition: background 50ms ease-in-out;
} }
.point-of-sale .order .orderline.empty:hover{
background: transparent;
cursor: default;
}
.point-of-sale .order .orderline.selected{ .point-of-sale .order .orderline.selected{
background: rgba(140,143,183,0.2); background: rgba(140,143,183,0.2);
@ -933,9 +1048,9 @@
background: -moz-linear-gradient(#f0f0f0, #e2e2e2); background: -moz-linear-gradient(#f0f0f0, #e2e2e2);
background: -ms-linear-gradient(#f0f0f0, #e2e2e2); background: -ms-linear-gradient(#f0f0f0, #e2e2e2);
background: linear-gradient(#f0f0f0, #e2e2e2); background: linear-gradient(#f0f0f0, #e2e2e2);
-webkit-box-shadow: 0px 2px 2px rgba(0,0,0, 0.3); -webkit-box-shadow: 0px 2px 2px rgba(0,0,0, 0.1);
-moz-box-shadow: 0px 2px 2px rgba(0,0,0, 0.3); -moz-box-shadow: 0px 2px 2px rgba(0,0,0, 0.1);
box-shadow: 0px 2px 2px rgba(0,0,0, 0.3); box-shadow: 0px 2px 2px rgba(0,0,0, 0.1);
} }
.point-of-sale .pos-actionbar .button .label{ .point-of-sale .pos-actionbar .button .label{
margin-top: 37px; margin-top: 37px;
@ -957,6 +1072,23 @@
-webkit-transition-timing-function: ease-out; -webkit-transition-timing-function: ease-out;
} }
.point-of-sale .pos-actionbar .button.disabled{
color:#AAA;
}
.point-of-sale .pos-actionbar .button.disabled:hover{
border: 1px solid #cacaca;
border-radius: 4px;
background: #e2e2e2;
background: -webkit-linear-gradient(#f0f0f0, #e2e2e2);
background: -moz-linear-gradient(#f0f0f0, #e2e2e2);
background: -ms-linear-gradient(#f0f0f0, #e2e2e2);
background: linear-gradient(#f0f0f0, #e2e2e2);
-webkit-box-shadow: 0px 2px 2px rgba(0,0,0, 0.1);
-moz-box-shadow: 0px 2px 2px rgba(0,0,0, 0.1);
box-shadow: 0px 2px 2px rgba(0,0,0, 0.1);
}
.point-of-sale .pos-actionbar .button.rightalign{ .point-of-sale .pos-actionbar .button.rightalign{
float:right; float:right;
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 46 KiB

View File

@ -0,0 +1,984 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="48"
height="48"
overflow="visible"
enable-background="new 0 0 128 129.396"
xml:space="preserve"
id="svg2"
sodipodi:version="0.32"
inkscape:version="0.48.3.1 r9886"
sodipodi:docname="scale.svg"
version="1.0"
inkscape:export-filename="/home/fva/Code/openerp/src/addons/trunk-pos-fva/point_of_sale/static/src/img/scale.png"
inkscape:export-xdpi="600"
inkscape:export-ydpi="600"
inkscape:output_extension="org.inkscape.output.svg.inkscape"><metadata
id="metadata367"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><cc:license
rdf:resource="http://creativecommons.org/licenses/publicdomain/" /><dc:title></dc:title><dc:creator><cc:Agent><dc:title>Jakub Steiner</dc:title></cc:Agent></dc:creator><dc:source>http://jimmac.musichall.cz</dc:source><dc:subject><rdf:Bag><rdf:li>home</rdf:li><rdf:li>return</rdf:li><rdf:li>go</rdf:li><rdf:li>default</rdf:li><rdf:li>user</rdf:li><rdf:li>directory</rdf:li></rdf:Bag></dc:subject><dc:contributor><cc:Agent><dc:title>Tuomas Kuosmanen</dc:title></cc:Agent></dc:contributor></cc:Work><cc:License
rdf:about="http://creativecommons.org/licenses/publicdomain/"><cc:permits
rdf:resource="http://creativecommons.org/ns#Reproduction" /><cc:permits
rdf:resource="http://creativecommons.org/ns#Distribution" /><cc:permits
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" /></cc:License></rdf:RDF></metadata><defs
id="defs365"><linearGradient
inkscape:collect="always"
id="linearGradient4977"><stop
style="stop-color:#ff0000;stop-opacity:1;"
offset="0"
id="stop4979" /><stop
style="stop-color:#ff0000;stop-opacity:0"
offset="1"
id="stop4981" /></linearGradient><inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="0 : 24 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="48 : 24 : 1"
inkscape:persp3d-origin="24 : 16 : 1"
id="perspective92" /><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient5060"
id="radialGradient5031"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(-2.774389,0,0,1.969706,112.7623,-872.8854)"
cx="605.71429"
cy="486.64789"
fx="605.71429"
fy="486.64789"
r="117.14286" /><linearGradient
inkscape:collect="always"
id="linearGradient5060"><stop
style="stop-color:black;stop-opacity:1;"
offset="0"
id="stop5062" /><stop
style="stop-color:black;stop-opacity:0;"
offset="1"
id="stop5064" /></linearGradient><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient5060"
id="radialGradient5029"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(2.774389,0,0,1.969706,-1891.633,-872.8854)"
cx="605.71429"
cy="486.64789"
fx="605.71429"
fy="486.64789"
r="117.14286" /><linearGradient
id="linearGradient5048"><stop
style="stop-color:black;stop-opacity:0;"
offset="0"
id="stop5050" /><stop
id="stop5056"
offset="0.5"
style="stop-color:black;stop-opacity:1;" /><stop
style="stop-color:black;stop-opacity:0;"
offset="1"
id="stop5052" /></linearGradient><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient5048"
id="linearGradient5027"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(2.774389,0,0,1.969706,-1892.179,-872.8854)"
x1="302.85715"
y1="366.64789"
x2="302.85715"
y2="609.50507" /><linearGradient
id="linearGradient2406"><stop
style="stop-color:#7c7e79;stop-opacity:1;"
offset="0"
id="stop2408" /><stop
id="stop2414"
offset="0.1724138"
style="stop-color:#848681;stop-opacity:1;" /><stop
style="stop-color:#898c86;stop-opacity:1;"
offset="1"
id="stop2410" /></linearGradient><linearGradient
inkscape:collect="always"
id="linearGradient2390"><stop
style="stop-color:#919191;stop-opacity:1;"
offset="0"
id="stop2392" /><stop
style="stop-color:#919191;stop-opacity:0;"
offset="1"
id="stop2394" /></linearGradient><linearGradient
inkscape:collect="always"
id="linearGradient2378"><stop
style="stop-color:#575757;stop-opacity:1;"
offset="0"
id="stop2380" /><stop
style="stop-color:#575757;stop-opacity:0;"
offset="1"
id="stop2382" /></linearGradient><linearGradient
inkscape:collect="always"
id="linearGradient2368"><stop
style="stop-color:#ffffff;stop-opacity:1;"
offset="0"
id="stop2370" /><stop
style="stop-color:#ffffff;stop-opacity:0;"
offset="1"
id="stop2372" /></linearGradient><linearGradient
inkscape:collect="always"
id="linearGradient2349"><stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop2351" /><stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop2353" /></linearGradient><linearGradient
id="linearGradient2341"><stop
id="stop2343"
offset="0"
style="stop-color:#000000;stop-opacity:1;" /><stop
id="stop2345"
offset="1"
style="stop-color:#000000;stop-opacity:0;" /></linearGradient><linearGradient
id="linearGradient2329"><stop
style="stop-color:#000000;stop-opacity:0.18556701;"
offset="0"
id="stop2331" /><stop
style="stop-color:#ffffff;stop-opacity:1;"
offset="1"
id="stop2333" /></linearGradient><linearGradient
inkscape:collect="always"
id="linearGradient2319"><stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop2321" /><stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop2323" /></linearGradient><linearGradient
id="linearGradient2307"><stop
style="stop-color:#edd400;stop-opacity:1;"
offset="0"
id="stop2309" /><stop
style="stop-color:#998800;stop-opacity:1;"
offset="1"
id="stop2311" /></linearGradient><linearGradient
inkscape:collect="always"
id="linearGradient2299"><stop
style="stop-color:#ffffff;stop-opacity:1;"
offset="0"
id="stop2301" /><stop
style="stop-color:#ffffff;stop-opacity:0;"
offset="1"
id="stop2303" /></linearGradient><linearGradient
id="XMLID_2_"
gradientUnits="userSpaceOnUse"
x1="80.223602"
y1="117.5205"
x2="48.046001"
y2="59.7995"
gradientTransform="matrix(0.314683,0.000000,0.000000,0.314683,4.128264,3.742874)">
<stop
offset="0"
style="stop-color:#CCCCCC"
id="stop17" />
<stop
offset="0.9831"
style="stop-color:#FFFFFF"
id="stop19" />
<midPointStop
offset="0"
style="stop-color:#CCCCCC"
id="midPointStop48" />
<midPointStop
offset="0.5"
style="stop-color:#CCCCCC"
id="midPointStop50" />
<midPointStop
offset="0.9831"
style="stop-color:#FFFFFF"
id="midPointStop52" />
</linearGradient><linearGradient
inkscape:collect="always"
xlink:href="#XMLID_2_"
id="linearGradient1514"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.336922,0,0,0.166888,143.49433,15.46151)"
x1="52.006104"
y1="166.1331"
x2="14.049017"
y2="-42.218513" /><linearGradient
id="XMLID_39_"
gradientUnits="userSpaceOnUse"
x1="64.387703"
y1="65.124001"
x2="64.387703"
y2="35.569"
gradientTransform="matrix(0.354101,0,0,0.354101,127.15013,-0.08364921)">
<stop
offset="0"
style="stop-color:#FFFFFF"
id="stop336" />
<stop
offset="0.8539"
style="stop-color:#FF6200"
id="stop338" />
<stop
offset="1"
style="stop-color:#F25D00"
id="stop340" />
<midPointStop
offset="0"
style="stop-color:#FFFFFF"
id="midPointStop335" />
<midPointStop
offset="0.5"
style="stop-color:#FFFFFF"
id="midPointStop337" />
<midPointStop
offset="0.8539"
style="stop-color:#FF6200"
id="midPointStop339" />
<midPointStop
offset="0.5"
style="stop-color:#FF6200"
id="midPointStop341" />
<midPointStop
offset="1"
style="stop-color:#F25D00"
id="midPointStop343" />
</linearGradient><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient2299"
id="radialGradient2305"
cx="7.5326638"
cy="24.202574"
fx="7.5326638"
fy="24.202574"
r="8.2452128"
gradientTransform="matrix(4.100086,0,0,4.201322,100.09639,-78.53967)"
gradientUnits="userSpaceOnUse" /><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient2307"
id="radialGradient2313"
cx="19.985598"
cy="36.77816"
fx="19.985598"
fy="36.77816"
r="1.0821035"
gradientTransform="matrix(1.125263,0,0,0.982744,122.08278,0.565787)"
gradientUnits="userSpaceOnUse" /><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient2319"
id="radialGradient2325"
cx="20.443665"
cy="37.425829"
fx="20.443665"
fy="37.425829"
r="1.0821035"
gradientTransform="matrix(1.125263,0,0,0.982744,122.08278,0.731106)"
gradientUnits="userSpaceOnUse" /><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient2329"
id="linearGradient2335"
x1="17.602522"
y1="26.057423"
x2="17.682528"
y2="32.654099"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.898789,0,0,1.071914,125.98948,-2.080838)" /><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient2341"
id="radialGradient2339"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(4.100086,0,0,-4.201322,120.31334,105.3535)"
cx="11.68129"
cy="19.554111"
fx="11.68129"
fy="19.554111"
r="8.2452126" /><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient2349"
id="radialGradient2355"
cx="24.023088"
cy="40.56913"
fx="24.023088"
fy="40.56913"
r="16.28684"
gradientTransform="matrix(1.000000,0.000000,0.000000,0.431250,1.157278e-15,23.07369)"
gradientUnits="userSpaceOnUse" /><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient2368"
id="radialGradient2374"
cx="29.913452"
cy="30.442923"
fx="29.913452"
fy="30.442923"
r="4.0018832"
gradientTransform="matrix(3.751495,0,0,3.147818,43.502383,-65.70704)"
gradientUnits="userSpaceOnUse" /><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient2378"
id="radialGradient2384"
cx="24.195112"
cy="10.577631"
fx="24.195112"
fy="10.577631"
r="15.242914"
gradientTransform="matrix(1.125263,-3.585417e-8,4.269819e-8,1.340059,122.50475,1.355395)"
gradientUnits="userSpaceOnUse" /><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient2390"
id="linearGradient2396"
x1="30.603519"
y1="37.337803"
x2="30.603519"
y2="36.112415"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.263867,0,0,0.859794,-6.499556,8.390924)" /><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient2406"
id="linearGradient2412"
x1="17.850183"
y1="28.939463"
x2="19.040216"
y2="41.03223"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.888785,0,0,1.08932,127.92244,-1.524336)" /><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient5048"
id="linearGradient4689"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(2.774389,0,0,1.969706,-1892.179,-872.8854)"
x1="302.85715"
y1="366.64789"
x2="302.85715"
y2="609.50507" /><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient5060"
id="radialGradient4691"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(2.774389,0,0,1.969706,-1891.633,-872.8854)"
cx="605.71429"
cy="486.64789"
fx="605.71429"
fy="486.64789"
r="117.14286" /><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient5060"
id="radialGradient4693"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(-2.774389,0,0,1.969706,112.7623,-872.8854)"
cx="605.71429"
cy="486.64789"
fx="605.71429"
fy="486.64789"
r="117.14286" /><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient5048"
id="linearGradient4894"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(2.774389,0,0,1.969706,-1892.179,-872.8854)"
x1="302.85715"
y1="366.64789"
x2="302.85715"
y2="609.50507" /><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient5060"
id="radialGradient4896"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(2.774389,0,0,1.969706,-1891.633,-872.8854)"
cx="605.71429"
cy="486.64789"
fx="605.71429"
fy="486.64789"
r="117.14286" /><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient5060"
id="radialGradient4898"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(-2.774389,0,0,1.969706,112.7623,-872.8854)"
cx="605.71429"
cy="486.64789"
fx="605.71429"
fy="486.64789"
r="117.14286" /><filter
inkscape:collect="always"
id="filter4971"
x="-0.064853556"
width="1.1297071"
y="-1.9456067"
height="4.8912134"><feGaussianBlur
inkscape:collect="always"
stdDeviation="0.81066946"
id="feGaussianBlur4973" /></filter><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4977"
id="linearGradient4983"
x1="23.6875"
y1="32"
x2="23.75"
y2="45.25"
gradientUnits="userSpaceOnUse" /></defs><sodipodi:namedview
inkscape:cy="-2.746436"
inkscape:cx="31.130127"
inkscape:zoom="8"
inkscape:window-height="1176"
inkscape:window-width="1855"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
borderopacity="0.21568627"
bordercolor="#666666"
pagecolor="#ffffff"
id="base"
inkscape:showpageshadow="false"
inkscape:window-x="65"
inkscape:window-y="24"
inkscape:current-layer="svg2"
fill="#555753"
showgrid="true"
stroke="#a40000"
showguides="true"
inkscape:guide-bbox="true"
inkscape:snap-global="false"
inkscape:window-maximized="1"><inkscape:grid
type="xygrid"
id="grid3818" /></sodipodi:namedview>
<g
style="display:inline"
id="g5022"
transform="matrix(0.02158196,0,0,0.01859457,168.63396,41.63767)"><rect
y="-150.69685"
x="-1559.2523"
height="478.35718"
width="1339.6335"
id="rect4173"
style="opacity:0.40206185;color:#000000;fill:url(#linearGradient5027);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible" /><path
sodipodi:nodetypes="cccc"
id="path5058"
d="m -219.61876,-150.68038 c 0,0 0,478.33079 0,478.33079 142.874166,0.90045 345.40022,-107.16966 345.40014,-239.196175 0,-132.026537 -159.436816,-239.134595 -345.40014,-239.134615 z"
style="opacity:0.40206185;color:#000000;fill:url(#radialGradient5029);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible"
inkscape:connector-curvature="0" /><path
style="opacity:0.40206185;color:#000000;fill:url(#radialGradient5031);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible"
d="m -1559.2523,-150.68038 c 0,0 0,478.33079 0,478.33079 -142.8742,0.90045 -345.4002,-107.16966 -345.4002,-239.196175 0,-132.026537 159.4368,-239.134595 345.4002,-239.134615 z"
id="path5018"
sodipodi:nodetypes="cccc"
inkscape:connector-curvature="0" /></g><path
style="color:#000000;fill:url(#linearGradient1514);fill-opacity:1;fill-rule:nonzero;stroke:#757575;stroke-width:1.0000006;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
d="m 147.13103,8.1833733 5.95746,0 c 0.83973,0 13.88647,15.4353277 13.88647,16.3406587 l -0.44352,18.496745 c 0,0.905333 -0.67603,1.634177 -1.51576,1.634177 l -31.45728,0 c -0.83973,0 -1.51576,-0.728844 -1.51576,-1.634177 l 0.0565,-18.496745 c 0,-0.905331 14.19218,-16.3406587 15.03191,-16.3406587 z"
id="rect1512"
sodipodi:nodetypes="ccccccccc"
inkscape:connector-curvature="0" /><path
style="fill:none"
id="path5"
d="m 172.47503,45.735573 -45.3249,0 0,-45.32489746 45.3249,0 0,45.32489746 z"
inkscape:connector-curvature="0" /><path
style="fill:url(#linearGradient2335);fill-opacity:1;fill-rule:evenodd"
id="path2327"
d="m 148.51145,29 -0.0457,15.090942 -11.84279,0 L 136.51145,29 l 12,0 z"
clip-rule="evenodd"
sodipodi:nodetypes="ccccc"
inkscape:connector-curvature="0" /><path
sodipodi:nodetypes="ccccccccc"
id="path2357"
d="m 147.29191,9.405584 5.5591,0 c 0.78358,0 13.00087,14.399588 13.00087,15.244172 l -0.34716,18.212311 c 0,0.459259 -0.14374,0.653465 -0.51237,0.653465 l -31.38721,0.01428 c -0.36863,0 -0.58396,-0.07992 -0.58396,-0.45355 l 0.21534,-18.426506 c 0,-0.844584 13.27181,-15.244172 14.05539,-15.244172 z"
style="opacity:0.3125;color:#000000;fill:none;stroke:#ffffff;stroke-width:1.00000012;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
inkscape:connector-curvature="0" /><path
clip-rule="evenodd"
d="m 132.71898,27.943053 -0.0543,2.595194 18.36817,-13.179254 15.28639,11.154428 0.0713,-0.311714 -16.37045,-15.904131 -17.30116,15.645477 z"
id="path23"
style="opacity:0.2;fill:url(#radialGradient2384);fill-opacity:1;fill-rule:evenodd"
sodipodi:nodetypes="ccccccc"
inkscape:connector-curvature="0" /><path
clip-rule="evenodd"
d="m 147.51145,30 0,14.090942 -9.81103,0 L 137.51145,30 l 10,0 z"
id="path188"
style="fill:url(#linearGradient2412);fill-opacity:1;fill-rule:evenodd"
sodipodi:nodetypes="ccccc"
inkscape:connector-curvature="0" /><path
style="opacity:0.40909089;fill:url(#radialGradient2325);fill-opacity:1;fill-rule:evenodd"
id="path2315"
d="m 145.08831,36.44767 c 0.67279,0 1.21662,0.474605 1.21662,1.058507 0,0.589811 -0.54383,1.068355 -1.21662,1.068355 -0.67227,0 -1.21869,-0.478544 -1.21869,-1.068355 5.2e-4,-0.583902 0.54642,-1.058507 1.21869,-1.058507 z"
clip-rule="evenodd"
inkscape:connector-curvature="0" /><path
clip-rule="evenodd"
d="m 144.97377,35.932229 c 0.67279,0 1.21661,0.474605 1.21661,1.058507 0,0.589809 -0.54382,1.068353 -1.21661,1.068353 -0.67228,0 -1.21869,-0.478544 -1.21869,-1.068353 5.2e-4,-0.583902 0.54641,-1.058507 1.21869,-1.058507 z"
id="path217"
style="fill:url(#radialGradient2313);fill-opacity:1;fill-rule:evenodd"
inkscape:connector-curvature="0" /><path
d="m 149.9592,11.559337 18.92706,17.169868 0.49468,0.391991 0.40368,-0.171385 -0.37287,-0.761673 -0.27762,-0.223436 -19.17493,-15.572306 -19.38951,15.743335 -0.23761,0.14412 -0.21671,0.706786 0.43342,0.129248 0.38456,-0.308423 19.02585,-17.248125 z"
id="path342"
style="fill:url(#XMLID_39_)"
sodipodi:nodetypes="ccccccccccccc"
inkscape:connector-curvature="0" /><path
style="fill:#ef2929;stroke:#a40000"
id="path362"
d="m 149.84162,2.2713382 -21.88174,18.1013368 -0.62473,7.165928 1.99994,2.064323 c 0,0 20.40738,-17.157285 20.62409,-17.327963 l 19.63254,17.54326 1.89843,-2.323997 -1.61579,-7.111374 -19.91518,-18.2159732 -0.11756,0.1044594 z"
sodipodi:nodetypes="cccccccccc"
inkscape:connector-curvature="0" />
<path
style="opacity:0.40909089;color:#000000;fill:url(#radialGradient2305);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible"
d="M 128.3528,20.613129 128.06124,27.236494 149.88067,8.980075 149.81034,3.0867443 128.3528,20.613129 z"
id="path1536"
sodipodi:nodetypes="ccccc"
inkscape:connector-curvature="0" /><path
sodipodi:nodetypes="ccccc"
id="path2337"
d="m 149.99522,8.7509884 0.0995,-5.8411017 19.32896,17.6519533 1.49181,6.500812 -20.92023,-18.3116636 z"
style="opacity:0.13636367;color:#000000;fill:url(#radialGradient2339);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible"
inkscape:connector-curvature="0" /><path
style="opacity:0.31818183;color:#000000;fill:none;stroke:#ffffff;stroke-width:0.99999934;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
d="m 152.61368,27.719824 9.04,0 c 0.77059,0 1.39096,0.62037 1.39096,1.390967 l -0.008,9.079221 c 0,0.770596 -0.59632,1.265969 -1.36691,1.265969 l -9.05609,0 c -0.77059,0 -1.39096,-0.620373 -1.39096,-1.390969 l 0,-8.954221 c 0,-0.770597 0.62037,-1.390967 1.39096,-1.390967 z"
id="rect2361"
sodipodi:nodetypes="ccccccccc"
inkscape:connector-curvature="0" /><rect
style="color:#000000;fill:#3465a4;fill-opacity:1;fill-rule:nonzero;stroke:#757575;stroke-width:0.9999994;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
id="rect3263"
width="10.001333"
height="9.9624557"
x="152.01923"
y="28.514256"
rx="0.38128215"
ry="0.38128215" /><path
style="opacity:0.39772728;color:#000000;fill:url(#radialGradient2374);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.99999958;marker:none;visibility:visible;display:inline;overflow:visible"
d="m 152.61857,34.408261 c 3.61798,0.331177 5.52773,-1.445704 8.86815,-1.55274 l 0.0247,-3.849491 -8.91161,-0.006 0.0187,5.408261 z"
id="rect2363"
sodipodi:nodetypes="ccccc"
inkscape:connector-curvature="0" /><g
transform="translate(65.937707,2.0108349)"
id="g4739"><path
sodipodi:type="arc"
style="fill:#6adb4d;fill-opacity:1;stroke:#34781d;stroke-width:1.05264175;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
id="path4741"
sodipodi:cx="25.367456"
sodipodi:cy="8.2694378"
sodipodi:rx="5.3033009"
sodipodi:ry="5.3033009"
d="m 30.670757,8.2694378 c 0,2.9289322 -2.374368,5.3033012 -5.303301,5.3033012 -2.928932,0 -5.3033,-2.374369 -5.3033,-5.3033012 0,-2.9289322 2.374368,-5.3033009 5.3033,-5.3033009 2.928933,0 5.303301,2.3743687 5.303301,5.3033009 z"
transform="matrix(0.95416667,0,0,0.94583333,1.405743,0.73519001)" /><path
transform="matrix(0.77083333,0,0,0.77916666,6.0564434,2.1576238)"
d="m 30.670757,8.2694378 c 0,2.9289322 -2.374368,5.3033012 -5.303301,5.3033012 -2.928932,0 -5.3033,-2.374369 -5.3033,-5.3033012 0,-2.9289322 2.374368,-5.3033009 5.3033,-5.3033009 2.928933,0 5.303301,2.3743687 5.303301,5.3033009 z"
sodipodi:ry="5.3033009"
sodipodi:rx="5.3033009"
sodipodi:cy="8.2694378"
sodipodi:cx="25.367456"
id="path4743"
style="fill:none;stroke:#b3ff9a;stroke-width:1.29034114;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
sodipodi:type="arc" /></g><g
id="g4733"
transform="translate(76.897863,5.0160386)"><path
transform="matrix(0.95416667,0,0,0.94583333,1.405743,0.73519001)"
d="m 30.670757,8.2694378 c 0,2.9289322 -2.374368,5.3033012 -5.303301,5.3033012 -2.928932,0 -5.3033,-2.374369 -5.3033,-5.3033012 0,-2.9289322 2.374368,-5.3033009 5.3033,-5.3033009 2.928933,0 5.303301,2.3743687 5.303301,5.3033009 z"
sodipodi:ry="5.3033009"
sodipodi:rx="5.3033009"
sodipodi:cy="8.2694378"
sodipodi:cx="25.367456"
id="path4735"
style="fill:#6adb4d;fill-opacity:1;stroke:#34781d;stroke-width:1.05264175;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
sodipodi:type="arc" /><path
sodipodi:type="arc"
style="fill:none;stroke:#b3ff9a;stroke-width:1.29034114;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
id="path4737"
sodipodi:cx="25.367456"
sodipodi:cy="8.2694378"
sodipodi:rx="5.3033009"
sodipodi:ry="5.3033009"
d="m 30.670757,8.2694378 c 0,2.9289322 -2.374368,5.3033012 -5.303301,5.3033012 -2.928932,0 -5.3033,-2.374369 -5.3033,-5.3033012 0,-2.9289322 2.374368,-5.3033009 5.3033,-5.3033009 2.928933,0 5.303301,2.3743687 5.303301,5.3033009 z"
transform="matrix(0.77083333,0,0,0.77916666,6.0564434,2.1576238)" /></g><g
transform="translate(70.887455,7.0268736)"
id="g4727"><path
sodipodi:type="arc"
style="fill:#6adb4d;fill-opacity:1;stroke:#34781d;stroke-width:1.05264175;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
id="path4729"
sodipodi:cx="25.367456"
sodipodi:cy="8.2694378"
sodipodi:rx="5.3033009"
sodipodi:ry="5.3033009"
d="m 30.670757,8.2694378 c 0,2.9289322 -2.374368,5.3033012 -5.303301,5.3033012 -2.928932,0 -5.3033,-2.374369 -5.3033,-5.3033012 0,-2.9289322 2.374368,-5.3033009 5.3033,-5.3033009 2.928933,0 5.303301,2.3743687 5.303301,5.3033009 z"
transform="matrix(0.95416667,0,0,0.94583333,1.405743,0.73519001)" /><path
transform="matrix(0.77083333,0,0,0.77916666,6.0564434,2.1576238)"
d="m 30.670757,8.2694378 c 0,2.9289322 -2.374368,5.3033012 -5.303301,5.3033012 -2.928932,0 -5.3033,-2.374369 -5.3033,-5.3033012 0,-2.9289322 2.374368,-5.3033009 5.3033,-5.3033009 2.928933,0 5.303301,2.3743687 5.303301,5.3033009 z"
sodipodi:ry="5.3033009"
sodipodi:rx="5.3033009"
sodipodi:cy="8.2694378"
sodipodi:cx="25.367456"
id="path4731"
style="fill:none;stroke:#b3ff9a;stroke-width:1.29034114;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
sodipodi:type="arc" /></g><g
id="g4723"
transform="translate(59.662134,5.9662134)"><path
transform="matrix(0.95416667,0,0,0.94583333,1.405743,0.73519001)"
d="m 30.670757,8.2694378 c 0,2.9289322 -2.374368,5.3033012 -5.303301,5.3033012 -2.928932,0 -5.3033,-2.374369 -5.3033,-5.3033012 0,-2.9289322 2.374368,-5.3033009 5.3033,-5.3033009 2.928933,0 5.303301,2.3743687 5.303301,5.3033009 z"
sodipodi:ry="5.3033009"
sodipodi:rx="5.3033009"
sodipodi:cy="8.2694378"
sodipodi:cx="25.367456"
id="path4719"
style="fill:#6adb4d;fill-opacity:1;stroke:#34781d;stroke-width:1.05264175;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
sodipodi:type="arc" /><path
sodipodi:type="arc"
style="fill:none;stroke:#b3ff9a;stroke-width:1.29034114;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
id="path4721"
sodipodi:cx="25.367456"
sodipodi:cy="8.2694378"
sodipodi:rx="5.3033009"
sodipodi:ry="5.3033009"
d="m 30.670757,8.2694378 c 0,2.9289322 -2.374368,5.3033012 -5.303301,5.3033012 -2.928932,0 -5.3033,-2.374369 -5.3033,-5.3033012 0,-2.9289322 2.374368,-5.3033009 5.3033,-5.3033009 2.928933,0 5.303301,2.3743687 5.303301,5.3033009 z"
transform="matrix(0.77083333,0,0,0.77916666,6.0564434,2.1576238)" /></g><g
id="g4695"
transform="matrix(1.1326331,0,0,0.44340824,65.580189,2.4215088)"><rect
style="fill:#8a8a8a;fill-opacity:1;stroke:none"
id="rect4697"
width="29.256544"
height="18.119614"
x="10.297243"
y="27.449707"
rx="1.7190553"
ry="4.3911204" /><g
id="g4699"><g
id="g4701"><rect
rx="1.7051755"
y="27.517153"
x="10.510214"
height="17.982044"
width="29.020325"
id="rect4703"
style="fill:none;stroke:#646464;stroke-width:1.41005611;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
ry="4.3556657" /></g><rect
rx="0.88575488"
y="29.624025"
x="11.337327"
height="13.83156"
width="27.319036"
id="rect4705"
style="fill:none;stroke:#b3b3b3;stroke-width:1.40048993;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
ry="2.2625546" /></g></g><g
transform="matrix(0.01936185,0,0,0.01859457,111.41659,40.621203)"
id="g4681"
style="display:inline"><rect
style="opacity:0.40206185;color:#000000;fill:url(#linearGradient4689);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible"
id="rect4683"
width="1339.6335"
height="478.35718"
x="-1559.2523"
y="-150.69685" /><path
inkscape:connector-curvature="0"
style="opacity:0.40206185;color:#000000;fill:url(#radialGradient4691);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible"
d="m -219.61876,-150.68038 c 0,0 0,478.33079 0,478.33079 142.874166,0.90045 345.40022,-107.16966 345.40014,-239.196175 0,-132.026537 -159.436816,-239.134595 -345.40014,-239.134615 z"
id="path4685"
sodipodi:nodetypes="cccc" /><path
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccc"
id="path4687"
d="m -1559.2523,-150.68038 c 0,0 0,478.33079 0,478.33079 -142.8742,0.90045 -345.4002,-107.16966 -345.4002,-239.196175 0,-132.026537 159.4368,-239.134595 345.4002,-239.134615 z"
style="opacity:0.40206185;color:#000000;fill:url(#radialGradient4693);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible" /></g><g
id="g4672"
transform="translate(68.942911,-2.9168155)"><rect
ry="1.9470588"
rx="1.9470588"
y="27.449707"
x="10.297243"
height="18.119614"
width="29.256544"
id="rect4658"
style="fill:#dddddd;fill-opacity:1;stroke:none" /><g
id="g4667"><g
id="g4664"><rect
ry="1.9313381"
style="fill:none;stroke:#646464;stroke-width:0.99927008;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
id="rect4660"
width="29.020325"
height="17.982044"
x="10.510214"
y="27.517153"
rx="1.9313381" /></g><rect
ry="1.0032353"
style="fill:none;stroke:#f6f6f6;stroke-width:0.99249077;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
id="rect4662"
width="26.967867"
height="16.005489"
x="11.532422"
y="28.489801"
rx="1.0032353" /></g></g><path
transform="matrix(1.0023678,0,0,0.95596394,68.883716,-0.0273148)"
d="m 35,33 a 10,10 0 1 1 -20,0 10,10 0 1 1 20,0 z"
sodipodi:ry="10"
sodipodi:rx="10"
sodipodi:cy="33"
sodipodi:cx="25"
id="path4679"
style="fill:#b8b8b8;fill-opacity:1;fill-rule:evenodd;stroke:none"
sodipodi:type="arc" /><path
sodipodi:type="arc"
style="fill:#b8b8b8;fill-opacity:1;fill-rule:evenodd;stroke:#646464;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
id="path3820"
sodipodi:cx="25"
sodipodi:cy="33"
sodipodi:rx="10"
sodipodi:ry="10"
d="m 35,33 a 10,10 0 1 1 -20,0 10,10 0 1 1 20,0 z"
transform="matrix(0.94933484,0,0,0.95596394,70.209541,-1.4857225)" /><path
transform="matrix(0.6928006,0,0,0.69727163,76.6008,8.0344445)"
d="m 35,33 a 10,10 0 1 1 -20,0 10,10 0 1 1 20,0 z"
sodipodi:ry="10"
sodipodi:rx="10"
sodipodi:cy="33"
sodipodi:cx="25"
id="path4590"
style="fill:#f4f4f4;fill-opacity:1;fill-rule:evenodd;stroke:none"
sodipodi:type="arc" /><g
id="g4644"
transform="matrix(0.70710678,0.70710678,-0.70710678,0.70710678,99.594592,-10.924363)"><rect
style="fill:#232323;fill-opacity:1;stroke:none"
id="rect4646"
width="0.68500972"
height="1.1490486"
x="24.638252"
y="26.853086" /><rect
y="37.990021"
x="24.660349"
height="1.1490486"
width="0.68500972"
id="rect4648"
style="fill:#232323;fill-opacity:1;stroke:none" /></g><g
id="g4600"
style="fill:#a2a2a2;fill-opacity:1"
transform="translate(69.009202,-2.3643883)"><rect
style="fill:#a2a2a2;fill-opacity:1;stroke:none"
id="rect4602"
width="1"
height="5.9375"
x="40.742825"
y="-1.6325631"
transform="matrix(0.67898035,0.73415645,-0.73415645,0.67898035,0,0)" /><path
sodipodi:type="arc"
style="fill:#a2a2a2;fill-opacity:1;stroke:none"
id="path4604"
sodipodi:cx="25.03125"
sodipodi:cy="33.03125"
sodipodi:rx="1.03125"
sodipodi:ry="0.96875"
d="M 26.0625,33.03125 C 26.0625,33.566276 25.600794,34 25.03125,34 24.461706,34 24,33.566276 24,33.03125 c 0,-0.535026 0.461706,-0.96875 1.03125,-0.96875 0.569544,0 1.03125,0.433724 1.03125,0.96875 z"
transform="matrix(0.95714504,0,0,1.0228099,1.028519,-0.77553647)" /></g><g
id="g4596"
transform="translate(68.942911,-2.9168155)"><rect
transform="matrix(0.67898035,0.73415645,-0.73415645,0.67898035,0,0)"
y="-1.6325631"
x="40.742825"
height="5.9375"
width="1"
id="rect4592"
style="fill:#ff2121;fill-opacity:1;stroke:none" /><path
transform="matrix(0.95714504,0,0,1.0228099,1.028519,-0.77553647)"
d="M 26.0625,33.03125 C 26.0625,33.566276 25.600794,34 25.03125,34 24.461706,34 24,33.566276 24,33.03125 c 0,-0.535026 0.461706,-0.96875 1.03125,-0.96875 0.569544,0 1.03125,0.433724 1.03125,0.96875 z"
sodipodi:ry="0.96875"
sodipodi:rx="1.03125"
sodipodi:cy="33.03125"
sodipodi:cx="25.03125"
id="path4594"
style="fill:#ff2121;fill-opacity:1;stroke:none"
sodipodi:type="arc" /></g><path
sodipodi:type="arc"
style="fill:none;stroke:#646464;stroke-width:1.37966764;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
id="path4608"
sodipodi:cx="25"
sodipodi:cy="33"
sodipodi:rx="10"
sodipodi:ry="10"
d="m 35,33 a 10,10 0 1 1 -20,0 10,10 0 1 1 20,0 z"
transform="matrix(0.74693846,0,0,0.74919978,75.258402,5.3595919)" /><g
id="g4628"
transform="translate(68.942911,-2.9168155)"><rect
y="26.853086"
x="24.638252"
height="1.1490486"
width="0.68500972"
id="rect4624"
style="fill:#232323;fill-opacity:1;stroke:none" /><rect
style="fill:#232323;fill-opacity:1;stroke:none"
id="rect4626"
width="0.68500972"
height="1.1490486"
x="24.660349"
y="37.990021" /></g><g
id="g4632"
transform="matrix(0,1,-1,0,126.93079,5.087457)"><rect
style="fill:#232323;fill-opacity:1;stroke:none"
id="rect4634"
width="0.68500972"
height="1.1490486"
x="24.638252"
y="26.853086" /><rect
y="37.990021"
x="24.660349"
height="1.1490486"
width="0.68500972"
id="rect4636"
style="fill:#232323;fill-opacity:1;stroke:none" /></g><rect
style="fill:#232323;fill-opacity:1;stroke:none"
id="rect4640"
width="0.68500972"
height="1.1490486"
x="-45.50618"
y="-93.834114"
transform="matrix(-0.70710678,0.70710678,-0.70710678,-0.70710678,0,0)" /><rect
y="-82.697182"
x="-45.484081"
height="1.1490486"
width="0.68500972"
id="rect4642"
style="fill:#232323;fill-opacity:1;stroke:none"
transform="matrix(-0.70710678,0.70710678,-0.70710678,-0.70710678,0,0)" /><g
transform="matrix(1.2021267,0,0,0.11694557,63.863529,11.401153)"
id="g4707"><rect
ry="16.649275"
rx="1.6196786"
y="27.449707"
x="10.297243"
height="18.119614"
width="29.256544"
id="rect4709"
style="fill:#d4d4d4;fill-opacity:1;stroke:none" /><g
id="g4711"><g
id="g4713"><rect
ry="16.514845"
style="fill:none;stroke:#646464;stroke-width:2.66511464;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
id="rect4715"
width="29.020325"
height="17.982044"
x="10.510214"
y="27.517153"
rx="1" /></g></g></g><g
id="g4985"
transform="translate(0,-11.125)"><g
transform="matrix(0.01936185,0,0,0.01859457,41.505849,40.621203)"
id="g4874"
style="display:inline"><rect
style="opacity:0.40206185;color:#000000;fill:url(#linearGradient4894);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible"
id="rect4876"
width="1339.6335"
height="478.35718"
x="-1559.2523"
y="-150.69685" /><path
inkscape:connector-curvature="0"
style="opacity:0.40206185;color:#000000;fill:url(#radialGradient4896);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible"
d="m -219.61876,-150.68038 c 0,0 0,478.33079 0,478.33079 142.874166,0.90045 345.40022,-107.16966 345.40014,-239.196175 0,-132.026537 -159.436816,-239.134595 -345.40014,-239.134615 z"
id="path4878"
sodipodi:nodetypes="cccc" /><path
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccc"
id="path4880"
d="m -1559.2523,-150.68038 c 0,0 0,478.33079 0,478.33079 -142.8742,0.90045 -345.4002,-107.16966 -345.4002,-239.196175 0,-132.026537 159.4368,-239.134595 345.4002,-239.134615 z"
style="opacity:0.40206185;color:#000000;fill:url(#radialGradient4898);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible" /></g><g
id="g4882"
transform="translate(-0.96783034,-2.9168155)"><rect
ry="1.9470588"
rx="1.9470588"
y="27.449707"
x="10.297243"
height="18.119614"
width="29.256544"
id="rect4884"
style="fill:#eaeaea;fill-opacity:1;stroke:none" /><g
id="g4886"><g
id="g4888"><rect
ry="1.9313381"
style="fill:none;stroke:#646464;stroke-width:0.99927008;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
id="rect4890"
width="29.020325"
height="17.982044"
x="10.510214"
y="27.517153"
rx="1.9313381" /></g><rect
ry="1.0032353"
style="fill:none;stroke:#ffffff;stroke-width:0.99249077;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
id="rect4892"
width="26.967867"
height="16.005489"
x="11.532422"
y="28.489801"
rx="1.0032353" /></g></g><g
transform="matrix(1,0,0,0.9039548,0,2.5932203)"
id="g4922"><rect
style="fill:#000000;fill-opacity:1;stroke:none"
id="rect4900"
width="1"
height="11.0625"
x="13.0625"
y="27" /><rect
y="27"
x="15.0625"
height="11.0625"
width="1"
id="rect4902"
style="fill:#000000;fill-opacity:1;stroke:none" /><rect
style="fill:#000000;fill-opacity:1;stroke:none"
id="rect4904"
width="1"
height="11.0625"
x="18"
y="27" /><rect
y="27"
x="19.9375"
height="11.0625"
width="2"
id="rect4906"
style="fill:#000000;fill-opacity:1;stroke:none" /><rect
y="27"
x="22.9375"
height="11.0625"
width="1"
id="rect4908"
style="fill:#000000;fill-opacity:1;stroke:none" /><rect
style="fill:#000000;fill-opacity:1;stroke:none"
id="rect4910"
width="2.0625"
height="11.0625"
x="25.9375"
y="27" /><rect
y="27"
x="28.9375"
height="11.0625"
width="1.125"
id="rect4912"
style="fill:#000000;fill-opacity:1;stroke:none" /><rect
style="fill:#000000;fill-opacity:1;stroke:none"
id="rect4914"
width="1.125"
height="11.0625"
x="30.9375"
y="27" /><rect
y="27"
x="33.9375"
height="11.0625"
width="1.125"
id="rect4916"
style="fill:#000000;fill-opacity:1;stroke:none" /></g><text
sodipodi:linespacing="125%"
id="text4918"
y="40.195068"
x="12.981453"
style="font-size:36.59571838px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
xml:space="preserve"><tspan
style="font-size:3.65957189px;font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;font-family:Inconsolata;-inkscape-font-specification:Inconsolata Medium"
y="40.195068"
x="12.981453"
id="tspan4920"
sodipodi:role="line">5 92102431 8</tspan></text>
<rect
y="31.0625"
x="9.0625"
height="1"
width="30"
id="rect4933"
style="fill:#ff0000;fill-opacity:1;stroke:none" /><rect
style="fill:#ff0000;fill-opacity:1;stroke:none;filter:url(#filter4971)"
id="rect4957"
width="30"
height="1"
x="9.0625"
y="31.0625" /><path
inkscape:connector-curvature="0"
id="path4975"
d="M 9.0625,32.0625 20,47.875 28.9375,47.9375 39,32.0625 z"
style="opacity:0.29787233;fill:url(#linearGradient4983);fill-opacity:1;stroke:none" /></g></svg>

After

Width:  |  Height:  |  Size: 41 KiB

View File

@ -0,0 +1,62 @@
v Affichage Catégories doivent respecter le poids
v Affichage Catégories par poids doivent avoir une hiérarchie plate
v Le Onscreen keyboard ne fonctionne plus
v Scrolling dans la sélection produit et dans
la liste de courses
v Réductions
v Redesign liste de courses
v bugs scans produits par prix par poids
v si pas photo photo par defaut
v WebSQL
v Redesign header, bouton retour aux backend en mode caissière
v bouton exit en mode caissière doit retourner au backend.
v posting orders
v bug ajout ligne payment à zero + curseur dedans + suppression + design plus grand
v si case print via proxy cochée, alors on skip l'ecran receipt
v bouton exit self checkout
v demarrage en mode self-checkout si self-checkout
v produits poid code barre non reconnus
v activer popup client aux écrans self-checkout
v discount pas plus grand que 100
v numpad dans l'écran payment pour pouvoir entrer le montant
v différence de total. Methode d'arrondis
- login alternatif
- user preferences : désactiver la balance etc.
- numpad state parfois sans state
- numpad state en mode pesée ?? le cacher ?
- Redesign du receipt
- retirer le receipt screen
- le numpad en mode paymenet marche pas
- l'écran de payement est hyper laid
- l'écran de pesée est hyper laid
x générer les données de printing
x popups d'erreur certainement buggés
TODO AUG 20
-----------
* GREEN STATUS
- make it green !
* TRUNK
- Connection status tooltip
v Remove receipt screen
- Finish the receipt JSON generation
- Modifie le widget de liste course
v supprimer l'écran
v bloquer sur l'impression
v L'impression est foireuse
* CLIENT
- create a new branch
- Self-checkout welcome screen
- removal of products for the root category
- Terminal de payement
- Code à barres
- Vidanges
- Design
- Backport
- CSS pour écran résistif
- Test sur matos

View File

@ -0,0 +1,235 @@
function openerp_pos_db(instance, module){
/* PosLS is a LocalStorage based implementation of the point of sale database,
it performs better for few products, but does not scale beyond 500 products.
*/
module.PosLS = instance.web.Class.extend({
name: 'openerp_pos_ls', //the prefix of the localstorage data
limit: 100, // the maximum number of results returned by a search
init: function(options){
options = options || {};
this.name = options.name || this.name;
this.limit = options.limit || this.limit;
//cache the data in memory to avoid roundtrips to the localstorage
this.cache = {};
this.category_by_id = {};
this.root_category_id = 0;
this.category_products = {};
this.category_ancestors = {};
this.category_childs = {};
this.category_parent = {};
this.category_search_string = {};
},
/* returns the category object from its id. If you pass a list of id as parameters, you get
* a list of category objects.
*/
get_category_by_id: function(categ_id){
if(categ_id instanceof Array){
var list = [];
for(var i = 0, len = categ_id.length; i < len; i++){
var cat = this.category_by_id[categ_id[i]];
if(cat){
list.push(cat);
}else{
console.error("get_category_by_id: no category has id:",categ_id[i]);
}
}
return list;
}else{
return this.category_by_id[categ_id];
}
},
/* returns a list of the category's child categories ids, or an empty list
* if a category has no childs */
get_category_childs_ids: function(categ_id){
return this.category_childs[categ_id] || [];
},
/* returns a list of all ancestors (parent, grand-parent, etc) categories ids
* starting from the root category to the direct parent */
get_category_ancestors_ids: function(categ_id){
return this.category_ancestors[categ_id] || [];
},
/* returns the parent category's id of a category, or the root_category_id if no parent.
* the root category is parent of itself. */
get_category_parent_id: function(categ_id){
return this.category_parent[categ_id] || this.root_category_id;
},
/* adds categories definitions to the database. categories is a list of categories objects as
* returned by the openerp server. Categories must be inserted before the products or the
* product/ categories association may (will) not work properly */
add_categories: function(categories){
var self = this;
if(!this.category_by_id[this.root_category_id]){
this.category_by_id[this.root_category_id] = {
id : this.root_category_id,
name : 'Root',
};
}
for(var i=0, len = categories.length; i < len; i++){
this.category_by_id[categories[i].id] = categories[i];
}
for(var i=0, len = categories.length; i < len; i++){
var cat = categories[i];
var parent_id = cat.parent_id[0] || this.root_category_id;
this.category_parent[cat.id] = cat.parent_id[0];
if(!this.category_childs[parent_id]){
this.category_childs[parent_id] = [];
}
this.category_childs[parent_id].push(cat.id);
}
function make_ancestors(cat_id, ancestors){
self.category_ancestors[cat_id] = ancestors;
ancestors = ancestors.slice(0);
ancestors.push(cat_id);
var childs = self.category_childs[cat_id] || [];
for(var i=0, len = childs.length; i < len; i++){
make_ancestors(childs[i], ancestors);
}
}
make_ancestors(this.root_category_id, []);
},
/* loads a record store from the database. returns default if nothing is found */
load: function(store,deft){
if(this.cache[store] !== undefined){
return this.cache[store];
}
var data = localStorage[this.name + '_' + store];
if(data !== undefined){
data = JSON.parse(data);
this.cache[store] = data;
return data;
}else{
return deft;
}
},
/* saves a record store to the database */
save: function(store,data){
localStorage[this.name + '_' + store] = JSON.stringify(data);
this.cache[store] = data;
},
_product_search_string: function(product){
var str = '' + product.id + ':' + product.name;
if(product.ean13){
str += '|' + product.ean13;
}
return str + '\n';
},
add_products: function(products){
var stored_products = this.load('products',{});
var stored_categories = this.load('categories',{});
if(!products instanceof Array){
products = [products];
}
for(var i = 0, len = products.length; i < len; i++){
var product = products[i];
var search_string = this._product_search_string(product);
var categ_id = product.pos_categ_id[0];
if(!stored_categories[categ_id]){
stored_categories[categ_id] = [];
}
stored_categories[categ_id].push(product.id);
if(this.category_search_string[categ_id] === undefined){
this.category_search_string[categ_id] = '';
}
this.category_search_string[categ_id] += search_string;
var ancestors = this.get_category_ancestors_ids(categ_id) || [];
for(var j = 0; j < ancestors.length; j++){
var ancestor = ancestors[j];
if(! stored_categories[ancestor]){
stored_categories[ancestor] = [];
}
stored_categories[ancestor].push(product.id);
if( this.category_search_string[ancestor] === undefined){
this.category_search_string[ancestor] = '';
}
this.category_search_string[ancestor] += search_string;
}
stored_products[product.id] = product;
}
this.save('products',stored_products);
this.save('categories',stored_categories);
},
/* removes all the data from the database. TODO : being able to selectively remove data */
clear: function(stores){
for(var i = 0, len = arguments.length; i < len; i++){
localStorage.removeItem(this.name + '_' + arguments[i]);
}
},
/* this internal methods returns the count of properties in an object. */
_count_props : function(obj){
var count = 0;
for(var prop in obj){
if(obj.hasOwnProperty(prop)){
count++;
}
}
return count;
},
get_product_by_id: function(id){
return this.load('products',{})[id];
},
get_product_by_ean13: function(ean13){
var products = this.load('products',{});
for(var i in products){
if( products[i] && products[i].ean13 === ean13){
return products[i];
}
}
return undefined;
},
get_product_by_category: function(category_id){
var stored_categories = this.load('categories',{});
var stored_products = this.load('products',{});
var product_ids = stored_categories[category_id];
var list = [];
for(var i = 0, len = Math.min(product_ids.length,this.limit); i < len; i++){
list.push(stored_products[product_ids[i]]);
}
return list;
},
/* returns a list of products with :
* - a category that is or is a child of category_id,
* - a name, package or ean13 containing the query (case insensitive)
*/
search_product_in_category: function(category_id, query){
var re = RegExp("([0-9]+):.*?"+query,"gi");
var results = [];
for(var i = 0; i < this.limit; i++){
r = re.exec(this.category_search_string[category_id]);
if(r){
var id = Number(r[1]);
results.push(this.get_product_by_id(id));
}else{
break;
}
}
return results;
},
add_order: function(order){
var last_id = this.load('last_order_id',0);
var orders = this.load('orders',[]);
orders.push({id: last_id + 1, data: order});
this.save('last_order_id',last_id+1);
this.save('orders',orders);
},
remove_order: function(order_id){
var orders = this.load('orders',[]);
orders = _.filter(orders, function(order){
return order.id !== order_id;
});
this.save('orders',orders);
},
get_orders: function(){
return this.load('orders',[]);
},
});
}

View File

@ -17,7 +17,9 @@ function openerp_pos_devices(instance,module){ //module is instance.point_of_sal
delay_payment: function(){ this.activate(); this.payment_status = 'waiting_for_payment'; }, delay_payment: function(){ this.activate(); this.payment_status = 'waiting_for_payment'; },
}))(); }))();
//window.debug_devices = debug_devices; if(jQuery.deparam(jQuery.param.querystring()).debug !== undefined){
window.debug_devices = debug_devices;
}
// this object interfaces with the local proxy to communicate to the various hardware devices // this object interfaces with the local proxy to communicate to the various hardware devices
// connected to the Point of Sale. As the communication only goes from the POS to the proxy, // connected to the Point of Sale. As the communication only goes from the POS to the proxy,
@ -42,21 +44,26 @@ function openerp_pos_devices(instance,module){ //module is instance.point_of_sal
success_callback = success_callback || function(){}; success_callback = success_callback || function(){};
error_callback = error_callback || function(){}; error_callback = error_callback || function(){};
if(debug_devices && debug_devices.active){
if(jQuery.deparam(jQuery.param.querystring()).debug !== undefined){
console.log('PROXY:',name,params); console.log('PROXY:',name,params);
}else{ }
if(!(debug_devices && debug_devices.active)){
this.connection.rpc('/pos/'+name, params || {}, success_callback, error_callback); this.connection.rpc('/pos/'+name, params || {}, success_callback, error_callback);
} }
}, },
//a product has been scanned and recognized with success //a product has been scanned and recognized with success
scan_item_success: function(){ // ean is a parsed ean object
this.message('scan_item_success'); scan_item_success: function(ean){
this.message('scan_item_success',ean);
}, },
//a product has been scanned but not recognized // a product has been scanned but not recognized
scan_item_error_unrecognized: function(){ // ean is a parsed ean object
this.message('scan_item_error_unrecognized'); scan_item_error_unrecognized: function(ean){
this.message('scan_item_error_unrecognized',ean);
}, },
//the client is asking for help //the client is asking for help
@ -222,11 +229,11 @@ function openerp_pos_devices(instance,module){ //module is instance.point_of_sal
this.action_callback_stack = []; this.action_callback_stack = [];
this.price_prefix_set = attributes.price_prefix_set || {'02':'', '22':'', '24':'', '26':'', '28':''}; this.weight_prefix_set = attributes.weight_prefix_set || {'21':''};
this.weight_prefix_set = attributes.weight_prefix_set || {'21':'','23':'','27':'','29':'','25':''}; this.discount_prefix_set = attributes.discount_prefix_set || {'22':''};
this.client_prefix_set = attributes.weight_prefix_set || {'42':''}; this.price_prefix_set = attributes.price_prefix_set || {'23':''};
this.cashier_prefix_set = attributes.weight_prefix_set || {'40':''}; this.cashier_prefix_set = attributes.cashier_prefix_set || {'041':''};
this.discount_prefix_set = attributes.weight_prefix_set || {'44':''}; this.client_prefix_set = attributes.client_prefix_set || {'042':''};
}, },
save_callbacks: function(){ save_callbacks: function(){
var callbacks = {}; var callbacks = {};
@ -270,41 +277,37 @@ function openerp_pos_devices(instance,module){ //module is instance.point_of_sal
this.action_callback[action] = undefined; this.action_callback[action] = undefined;
} }
}, },
// returns the checksum of the ean, or -1 if the ean has not the correct length, ean must be a string
ean_checksum: function(ean){
var code = ean.split('');
if(code.length !== 13){
return -1;
}
var oddsum = 0, evensum = 0, total = 0;
code = code.reverse().splice(1);
for(var i = 0; i < code.length; i++){
if(i % 2 == 0){
oddsum += Number(code[i]);
}else{
evensum += Number(code[i]);
}
}
total = oddsum * 3 + evensum;
return Number((10 - total % 10) % 10);
},
// returns true if the ean is a valid EAN codebar number by checking the control digit. // returns true if the ean is a valid EAN codebar number by checking the control digit.
// ean must be a string // ean must be a string
check_ean: function(ean){ check_ean: function(ean){
var code = ean.split(''); return this.ean_checksum(ean) === Number(ean[ean.length-1]);
for(var i = 0; i < code.length; i++){ },
code[i] = Number(code[i]); // returns a valid zero padded ean13 from an ean prefix. the ean prefix must be a string.
sanitize_ean:function(ean){
ean = ean.substr(0,13);
for(var n = 0, count = (13 - ean.length); n < count; n++){
ean = ean + '0';
} }
var st1 = code.slice(); return ean.substr(0,12) + this.ean_checksum(ean);
var st2 = st1.slice(0,st1.length-1).reverse();
// some EAN13 barcodes have a length of 12, as they start by 0
while (st2.length < 12) {
st2.push(0);
}
var countSt3 = 1;
var st3 = 0;
$.each(st2, function() {
if (countSt3%2 === 1) {
st3 += this;
}
countSt3 ++;
});
st3 *= 3;
var st4 = 0;
var countSt4 = 1;
$.each(st2, function() {
if (countSt4%2 === 0) {
st4 += this;
}
countSt4 ++;
});
var st5 = st3 + st4;
var cd = (10 - (st5%10)) % 10;
return code[code.length-1] === cd;
}, },
// attempts to interpret an ean (string encoding an ean) // attempts to interpret an ean (string encoding an ean)
@ -327,41 +330,47 @@ function openerp_pos_devices(instance,module){ //module is instance.point_of_sal
type:'unknown', // type:'unknown', //
prefix:'', prefix:'',
ean:ean, ean:ean,
base_ean: ean,
id:'', id:'',
value: 0, value: 0,
unit: 'none', unit: 'none',
}; };
var prefix2 = ean.substring(0,2); console.log('ean',ean);
if(!this.check_ean(ean)){ function match_prefix(prefix_set, type){
for(prefix in prefix_set){
if(ean.substring(0,prefix.length) === prefix){
parse_result.prefix = prefix;
parse_result.type = type;
return true;
}
}
return false;
}
if (!this.check_ean(ean)){
parse_result.type = 'error'; parse_result.type = 'error';
}else if (prefix2 in this.price_prefix_set){ } else if( match_prefix(this.price_prefix_set,'price')){
parse_result.type = 'price';
parse_result.prefix = prefix2;
parse_result.id = ean.substring(0,7); parse_result.id = ean.substring(0,7);
parse_result.base_ean = this.sanitize_ean(ean.substring(0,7));
parse_result.value = Number(ean.substring(7,12))/100.0; parse_result.value = Number(ean.substring(7,12))/100.0;
parse_result.unit = 'euro'; parse_result.unit = 'euro';
} else if (prefix2 in this.weight_prefix_set){ } else if( match_prefix(this.weight_prefix_set,'weight')){
parse_result.type = 'weight';
parse_result.prefix = prefix2;
parse_result.id = ean.substring(0,7); parse_result.id = ean.substring(0,7);
parse_result.value = Number(ean.substring(7,12))/1000.0; parse_result.value = Number(ean.substring(7,12))/1000.0;
parse_result.base_ean = this.sanitize_ean(ean.substring(0,7));
parse_result.unit = 'Kg'; parse_result.unit = 'Kg';
}else if (prefix2 in this.client_prefix_set){ } else if( match_prefix(this.client_prefix_set,'client')){
parse_result.type = 'client';
parse_result.prefix = prefix2;
parse_result.id = ean.substring(0,7); parse_result.id = ean.substring(0,7);
}else if (prefix2 in this.cashier_prefix_set){ parse_result.unit = 'Kg';
parse_result.type = 'cashier'; } else if( match_prefix(this.cashier_prefix_set,'cashier')){
parse_result.prefix = prefix2;
parse_result.id = ean.substring(0,7); parse_result.id = ean.substring(0,7);
}else if (prefix2 in this.discount_prefix_set){ } else if( match_prefix(this.discount_prefix_set,'discount')){
parse_result.type = 'discount';
parse_result.prefix = prefix2;
parse_result.id = ean.substring(0,7); parse_result.id = ean.substring(0,7);
parse_result.base_ean = this.sanitize_ean(ean.substring(0,7));
parse_result.value = Number(ean.substring(7,12))/100.0; parse_result.value = Number(ean.substring(7,12))/100.0;
parse_result.unit = '%'; parse_result.unit = '%';
}else{ } else {
parse_result.type = 'unit'; parse_result.type = 'unit';
parse_result.prefix = ''; parse_result.prefix = '';
parse_result.id = ean; parse_result.id = ean;
@ -402,7 +411,7 @@ function openerp_pos_devices(instance,module){ //module is instance.point_of_sal
var parse_result = self.parse_ean(codeNumbers.join('')); var parse_result = self.parse_ean(codeNumbers.join(''));
if (parse_result.type === 'error') { //most likely a checksum error, raise warning if (parse_result.type === 'error') { //most likely a checksum error, raise warning
console.error('ERROR: barcode checksum error:',parse_result); console.warn('WARNING: barcode checksum error:',parse_result);
}else if(parse_result.type in {'unit':'', 'weight':'', 'price':''}){ //ean is associated to a product }else if(parse_result.type in {'unit':'', 'weight':'', 'price':''}){ //ean is associated to a product
if(self.action_callback['product']){ if(self.action_callback['product']){
self.action_callback['product'](parse_result); self.action_callback['product'](parse_result);

View File

@ -5,6 +5,8 @@ openerp.point_of_sale = function(instance) {
var module = instance.point_of_sale; var module = instance.point_of_sale;
openerp_pos_db(instance,module); // import db.js
openerp_pos_models(instance,module); // import pos_models.js openerp_pos_models(instance,module); // import pos_models.js
openerp_pos_basewidget(instance,module); // import pos_basewidget.js openerp_pos_basewidget(instance,module); // import pos_basewidget.js

View File

@ -1,51 +1,6 @@
function openerp_pos_models(instance, module){ //module is instance.point_of_sale function openerp_pos_models(instance, module){ //module is instance.point_of_sale
var QWeb = instance.web.qweb; var QWeb = instance.web.qweb;
module.LocalStorageDAO = instance.web.Class.extend({
add_operation: function(operation) {
var self = this;
return $.async_when().pipe(function() {
var tmp = self._get('oe_pos_operations', []);
var last_id = self._get('oe_pos_operations_sequence', 1);
tmp.push({'id': last_id, 'data': operation});
self._set('oe_pos_operations', tmp);
self._set('oe_pos_operations_sequence', last_id + 1);
});
},
remove_operation: function(id) {
var self = this;
return $.async_when().pipe(function() {
var tmp = self._get('oe_pos_operations', []);
tmp = _.filter(tmp, function(el) {
return el.id !== id;
});
self._set('oe_pos_operations', tmp);
});
},
get_operations: function() {
var self = this;
return $.async_when().pipe(function() {
return self._get('oe_pos_operations', []);
});
},
_get: function(key, default_) {
var txt = localStorage['oe_pos_dao_'+key];
if (! txt)
return default_;
return JSON.parse(txt);
},
_set: function(key, value) {
localStorage['oe_pos_dao_'+key] = JSON.stringify(value);
},
reset_stored_data: function(){
for(key in localStorage){
if(key.indexOf('oe_pos_dao_') === 0){
delete localStorage[key];
}
}
},
});
var fetch = function(model, fields, domain, ctx){ var fetch = function(model, fields, domain, ctx){
return new instance.web.Model(model).query(fields).filter(domain).context(ctx).all() return new instance.web.Model(model).query(fields).filter(domain).context(ctx).all()
}; };
@ -58,29 +13,20 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
// this is done asynchronously, a ready deferred alows the GUI to wait interactively // this is done asynchronously, a ready deferred alows the GUI to wait interactively
// for the loading to be completed // for the loading to be completed
// There is a single instance of the PosModel for each Front-End instance, it is usually called // There is a single instance of the PosModel for each Front-End instance, it is usually called
// 'pos' and is available to almost all widgets. // 'pos' and is available to all widgets extending PosWidget.
module.PosModel = Backbone.Model.extend({ module.PosModel = Backbone.Model.extend({
initialize: function(session, attributes) { initialize: function(session, attributes) {
Backbone.Model.prototype.initialize.call(this, attributes); Backbone.Model.prototype.initialize.call(this, attributes);
var self = this; var self = this;
this.dao = new module.LocalStorageDAO(); // used to store the order's data on the Hard Drive this.session = session;
this.ready = $.Deferred(); // used to notify the GUI that the PosModel has loaded all resources this.ready = $.Deferred(); // used to notify the GUI that the PosModel has loaded all resources
this.flush_mutex = new $.Mutex(); // used to make sure the orders are sent to the server once at time this.flush_mutex = new $.Mutex(); // used to make sure the orders are sent to the server once at time
//this.build_tree = _.bind(this.build_tree, this); // ???
this.session = session;
this.categories = {};
this.root_category = null;
this.weightable_categories = []; // a flat list of all categories that directly contain weightable products
this.barcode_reader = new module.BarcodeReader({'pos': this}); // used to read barcodes
this.proxy = new module.ProxyDevice(); // used to communicate to the hardware devices via a local proxy
// pos settings this.barcode_reader = new module.BarcodeReader({'pos': this}); // used to read barcodes
this.use_scale = false; this.proxy = new module.ProxyDevice(); // used to communicate to the hardware devices via a local proxy
this.use_proxy_printer = false; this.db = new module.PosLS(); // a database used to store the products and categories
this.use_virtual_keyboard = false; this.db.clear('products','categories');
this.use_websql = false;
this.use_barcode_scanner = false;
// default attributes values. If null, it will be loaded below. // default attributes values. If null, it will be loaded below.
this.set({ this.set({
@ -98,12 +44,12 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
'products': new module.ProductCollection(), 'products': new module.ProductCollection(),
'cashRegisters': null, 'cashRegisters': null,
'product_list': null, // the list of all products, does not change.
'bank_statements': null, 'bank_statements': null,
'taxes': null, 'taxes': null,
'pos_session': null, 'pos_session': null,
'pos_config': null, 'pos_config': null,
'categories': null, 'units': null,
'units_by_id': null,
'selectedOrder': undefined, 'selectedOrder': undefined,
}); });
@ -113,10 +59,9 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
// We fetch the backend data on the server asynchronously. this is done only when the pos user interface is launched, // We fetch the backend data on the server asynchronously. this is done only when the pos user interface is launched,
// Any change on this data made on the server is thus not reflected on the point of sale until it is relaunched. // Any change on this data made on the server is thus not reflected on the point of sale until it is relaunched.
var user_def = fetch('res.users',['name','company_id'],[['id','=',this.session.uid]]) var loaded = fetch('res.users',['name','company_id'],[['id','=',this.session.uid]])
.pipe(function(users){ .pipe(function(users){
var user = users[0]; self.set('user',users[0]);
self.set('user',user);
return fetch('res.company', return fetch('res.company',
[ [
@ -124,171 +69,114 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
'email', 'email',
'website', 'website',
'company_registry', 'company_registry',
//TODO contact_address
'vat', 'vat',
'name', 'name',
'phone' 'phone',
'partner_id',
], ],
[['id','=',user.company_id[0]]]) [['id','=',users[0].company_id[0]]]);
}).pipe(function(companies){ }).pipe(function(companies){
var company = companies[0]; self.set('company',companies[0]);
self.set('company',company);
return fetch('res.currency',['symbol','position'],[['id','=',company.currency_id[0]]]); return fetch('res.partner',['contact_address'],[['id','=',companies[0].partner_id[0]]]);
}).pipe(function (currencies){ }).pipe(function(company_partners){
self.get('company').contact_address = company_partners[0].contact_address;
return fetch('res.currency',['symbol','position'],[['id','=',self.get('company').currency_id[0]]]);
}).pipe(function(currencies){
self.set('currency',currencies[0]); self.set('currency',currencies[0]);
});
var cat_def = fetch('pos.category', ['id','name', 'parent_id', 'child_id', 'image_medium']) return fetch('product.uom', null, null);
.pipe(function(result){ }).pipe(function(units){
return self.set({'categories': result}); self.set('units',units);
});
var uom_def = fetch( //unit of measure
'product.uom',
null,
null
).then(function(result){
self.set({'units': result});
var units_by_id = {}; var units_by_id = {};
for(var i = 0, len = result.length; i < len; i++){ for(var i = 0, len = units.length; i < len; i++){
units_by_id[result[i].id] = result[i]; units_by_id[units[i].id] = units[i];
} }
self.set({'units_by_id':units_by_id}); self.set('units_by_id',units_by_id);
});
var pack_def = fetch(
'product.packaging',
null,
null
).then(function(packaging){
self.set('product.packaging',packaging);
});
var users_def = fetch(
'res.users',
['name','ean13'],
[['ean13', '!=', false]]
).then(function(result){
self.set({'user_list':result});
});
var tax_def = fetch('account.tax', ['amount','price_include','type'])
.then(function(result){
self.set({'taxes': result});
});
var session_def = fetch( // loading the PoS Session.
'pos.session',
['id', 'journal_ids','name','user_id','config_id','start_at','stop_at'],
[['state', '=', 'opened'], ['user_id', '=', this.session.uid]]
).pipe(function(result) {
// some data are associated with the pos session, like the pos config and bank statements.
// we must have a valid session before we can read those.
var session_data_def = new $.Deferred(); return fetch('product.packaging', null, null);
}).pipe(function(packagings){
self.set('product.packaging',packagings);
if( result.length !== 0 ) { return fetch('res.users', ['name','ean13'], [['ean13', '!=', false]]);
var pos_session = result[0]; }).pipe(function(users){
self.set('user_list',users);
self.set({'pos_session': pos_session}); return fetch('account.tax', ['amount', 'price_include', 'type']);
}).pipe(function(taxes){
self.set('taxes', taxes);
var pos_config_def = fetch( return fetch(
'pos.config', 'pos.session',
['name','journal_ids','shop_id','journal_id', ['id', 'journal_ids','name','user_id','config_id','start_at','stop_at'],
'iface_self_checkout', 'iface_websql', 'iface_led', 'iface_cashdrawer', [['state', '=', 'opened'], ['user_id', '=', self.session.uid]]
'iface_payment_terminal', 'iface_electronic_scale', 'iface_barscan', 'iface_vkeyboard', );
'iface_print_via_proxy','iface_cashdrawer','state','sequence_id','session_ids'], }).pipe(function(sessions){
[['id','=', pos_session.config_id[0]]] self.set('pos_session', sessions[0]);
).pipe(function(result){
var pos_config = result[0]
self.set({'pos_config': pos_config});
self.use_scale = pos_config.iface_electronic_scale || false;
self.use_proxy_printer = pos_config.iface_print_via_proxy || false;
self.use_virtual_keyboard = pos_config.iface_vkeyboard || false;
self.use_websql = pos_config.iface_websql || false;
self.use_barcode_scanner = pos_config.iface_barscan || false;
self.use_selfcheckout = pos_config.iface_self_checkout || false;
self.use_cashbox = pos_config.iface_cashdrawer || false;
return shop_def = fetch('sale.shop',[], [['id','=',pos_config.shop_id[0]]]) return fetch(
}).pipe(function(shops){ 'pos.config',
self.set('shop',shops[0]); ['name','journal_ids','shop_id','journal_id',
return fetch( 'iface_self_checkout', 'iface_led', 'iface_cashdrawer',
'product.product', 'iface_payment_terminal', 'iface_electronic_scale', 'iface_barscan', 'iface_vkeyboard',
//context {pricelist: shop.pricelist_id[0]} 'iface_print_via_proxy','iface_cashdrawer','state','sequence_id','session_ids'],
['name', 'list_price','price','pos_categ_id', 'taxes_id','image_medium', 'ean13', 'to_weight', 'uom_id', 'uos_id', 'uos_coeff', 'mes_type'], [['id','=', self.get('pos_session').config_id[0]]]
[['pos_categ_id','!=', false]], );
{pricelist: shops[0].pricelist_id[0]} // context for price }).pipe(function(configs){
); var pos_config = configs[0];
}).pipe( function(product_list){ self.set('pos_config', pos_config);
self.set({'product_list': product_list}); self.iface_electronic_scale = !!pos_config.iface_electronic_scale;
}); self.iface_print_via_proxy = !!pos_config.iface_print_via_proxy;
self.iface_vkeyboard = !!pos_config.iface_vkeyboard;
self.iface_self_checkout = !!pos_config.iface_self_checkout;
self.iface_cashdrawer = !!pos_config.iface_cashdrawer;
var bank_def = fetch( return fetch('sale.shop',[],[['id','=',pos_config.shop_id[0]]]);
'account.bank.statement', }).pipe(function(shops){
['account_id','currency','journal_id','state','name','user_id','pos_session_id'], self.set('shop',shops[0]);
[['state','=','open'],['pos_session_id', '=', pos_session.id]]
).then(function(result){
self.set({'bank_statements':result});
});
var journal_def = fetch( return fetch('pos.category', ['id','name','parent_id','child_id','image'])
'account.journal', }).pipe(function(categories){
undefined, self.db.add_categories(categories);
[['user_id','=',pos_session.user_id[0]]]
).then(function(result){
self.set({'journals':result});
});
// associate the bank statements with their journals. return fetch(
var bank_process_def = $.when(bank_def, journal_def) 'product.product',
.then(function(){ ['name', 'list_price','price','pos_categ_id', 'taxes_id', 'ean13', 'to_weight', 'uom_id', 'uos_id', 'uos_coeff', 'mes_type'],
var bank_statements = self.get('bank_statements'); [['pos_categ_id','!=', false]],
var journals = self.get('journals'); {pricelist: self.get('shop').pricelist_id[0]} // context for price
for(var i = 0, ilen = bank_statements.length; i < ilen; i++){ );
for(var j = 0, jlen = journals.length; j < jlen; j++){ }).pipe(function(products){
if(bank_statements[i].journal_id[0] === journals[j].id){ self.db.add_products(products);
bank_statements[i].journal = journals[j];
bank_statements[i].self_checkout_payment_method = journals[j].self_checkout_payment_method;
}
}
}
});
session_data_def = $.when(pos_config_def,bank_def,journal_def,bank_process_def); return fetch(
'account.bank.statement',
['account_id','currency','journal_id','state','name','user_id','pos_session_id'],
[['state','=','open'],['pos_session_id', '=', self.get('pos_session').id]]
);
}).pipe(function(bank_statements){
self.set('bank_statements', bank_statements);
}else{ return fetch('account.journal', undefined, [['user_id','=', self.get('pos_session').user_id[0]]]);
session_data_def.reject(); }).pipe(function(journals){
} self.set('journals',journals);
return session_data_def;
}); // associate the bank statements with their journals.
var bank_statements = self.get('bank_statements');
// associate the products with their categories for(var i = 0, ilen = bank_statements.length; i < ilen; i++){
var prod_process_def = $.when(cat_def, session_def) for(var j = 0, jlen = journals.length; j < jlen; j++){
.pipe(function(){ if(bank_statements[i].journal_id[0] === journals[j].id){
var product_list = self.get('product_list'); bank_statements[i].journal = journals[j];
var categories = self.get('categories'); bank_statements[i].self_checkout_payment_method = journals[j].self_checkout_payment_method;
var cat_by_id = {}; }
for(var i = 0; i < categories.length; i++){ }
cat_by_id[categories[i].id] = categories[i];
}
//set the parent in the category
for(var i = 0; i < categories.length; i++){
categories[i].parent_category = cat_by_id[categories[i].parent_id[0]];
}
for(var i = 0; i < product_list.length; i++){
product_list[i].pos_category = cat_by_id[product_list[i].pos_categ_id[0]];
} }
self.set({'cashRegisters' : new module.CashRegisterCollection(self.get('bank_statements'))});
}); });
// when all the data has loaded, we compute some stuff, and declare the Pos ready to be used. // when all the data has loaded, we compute some stuff, and declare the Pos ready to be used.
$.when(pack_def, cat_def, user_def, users_def, uom_def, session_def, tax_def, prod_process_def, user_def, this.flush()) $.when(loaded)
.then(function(){ .then(function(){
self.build_categories();
self.set({'cashRegisters' : new module.CashRegisterCollection(self.get('bank_statements'))});
//self.log_loaded_data(); //Uncomment if you want to log the data to the console for easier debugging //self.log_loaded_data(); //Uncomment if you want to log the data to the console for easier debugging
self.ready.resolve(); self.ready.resolve();
},function(){ },function(){
@ -302,7 +190,6 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
log_loaded_data: function(){ log_loaded_data: function(){
console.log('PosModel data has been loaded:'); console.log('PosModel data has been loaded:');
console.log('PosModel: categories:',this.get('categories')); console.log('PosModel: categories:',this.get('categories'));
console.log('PosModel: product_list:',this.get('product_list'));
console.log('PosModel: units:',this.get('units')); console.log('PosModel: units:',this.get('units'));
console.log('PosModel: bank_statements:',this.get('bank_statements')); console.log('PosModel: bank_statements:',this.get('bank_statements'));
console.log('PosModel: journals:',this.get('journals')); console.log('PosModel: journals:',this.get('journals'));
@ -316,7 +203,6 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
console.log('PosModel: user_list:',this.get('user_list')); console.log('PosModel: user_list:',this.get('user_list'));
console.log('PosModel: user:',this.get('user')); console.log('PosModel: user:',this.get('user'));
console.log('PosModel.session:',this.session); console.log('PosModel.session:',this.session);
console.log('PosModel.categories:',this.categories);
console.log('PosModel end of data log.'); console.log('PosModel end of data log.');
}, },
@ -324,7 +210,7 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
// order and a valid selected order // order and a valid selected order
on_removed_order: function(removed_order){ on_removed_order: function(removed_order){
if( this.get('orders').isEmpty()){ if( this.get('orders').isEmpty()){
this.add_and_select_order(new module.Order({ pos: this })); this.add_new_order();
} }
if( this.get('selectedOrder') === removed_order){ if( this.get('selectedOrder') === removed_order){
this.set({ selectedOrder: this.get('orders').last() }); this.set({ selectedOrder: this.get('orders').last() });
@ -333,175 +219,70 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
// saves the order locally and try to send it to the backend. 'record' is a bizzarely defined JSON version of the Order // saves the order locally and try to send it to the backend. 'record' is a bizzarely defined JSON version of the Order
push_order: function(record) { push_order: function(record) {
var self = this; this.db.add_order(record);
return this.dao.add_operation(record).pipe(function(){ this.flush();
return self.flush();
});
}, },
add_and_select_order: function(newOrder) { //creates a new empty order and sets it as the current order
(this.get('orders')).add(newOrder); add_new_order: function(){
return this.set({ var order = new module.Order({pos:this});
selectedOrder: newOrder this.get('orders').add(order);
}); this.set('selectedOrder', order);
}, },
// attemps to send all pending orders ( stored in the DAO ) to the server. // attemps to send all pending orders ( stored in the pos_db ) to the server,
// it will do it one by one, and remove the successfully sent ones from the DAO once // and remove the successfully sent ones from the db once
// it has been confirmed that they have been received. // it has been confirmed that they have been sent correctly.
flush: function() { flush: function() {
//this makes sure only one _int_flush is called at the same time //this makes sure only one _int_flush is called at the same time
return this.flush_mutex.exec(_.bind(function() { return this.flush_mutex.exec(_.bind(function() {
return this._int_flush(); return this._flush(0);
}, this)); }, this));
}, },
_int_flush : function() { // attempts to send an order of index 'index' in the list of order to send. The index
// is used to skip orders that failed. do not call this method outside the mutex provided
// by flush()
_flush: function(index){
var self = this; var self = this;
var orders = this.db.get_orders();
self.set('nbr_pending_operations',orders.length);
this.dao.get_operations().pipe(function(operations) { var order = orders[index];
// operations are really Orders that are converted to json. if(!order){
// they are saved to disk and then we attempt to send them to the backend so that they can return;
// be applied. }
// since the network is not reliable we potentially have many 'pending operations' that have not been sent. //try to push an order to the server
self.set( {'nbr_pending_operations':operations.length} ); (new instance.web.Model('pos.order')).get_func('create_from_ui')([order])
if(operations.length === 0){ .fail(function(unused, event){
return $.when(); //don't show error popup if it fails
} event.preventDefault();
var order = operations[0]; console.error('Failed to send order:',order);
self._flush(index+1);
// we prevent the default error handler and assume errors })
// are a normal use case, except we stop the current iteration .done(function(){
//remove from db if success
return (new instance.web.Model('pos.order')).get_func('create_from_ui')([order]) self.db.remove_order(order.id);
.fail(function(unused, event){ self._flush(index);
// wtf ask niv });
event.preventDefault();
})
.pipe(function(){
// success: remove the successfully sent operation, and try to send the next one
self.dao.remove_operation(operations[0].id).pipe(function(){
return self._int_flush();
});
}, function(){
// in case of error we just sit there and do nothing. wtf ask niv
return $.when();
});
});
}, },
// this adds several properties to the categories in order to make it easier to diplay them scan_product: function(parsed_ean){
// fields added include the list of product relevant to each category, list of child categories, var self = this;
// list of ancestors, etc. var product = this.db.get_product_by_ean13(parsed_ean.base_ean);
build_categories : function(){ var selectedOrder = this.get('selectedOrder');
var categories = this.get('categories');
var products = this.get('product_list');
//append the content of array2 into array1 if(!product){
function append(array1, array2){ return false;
for(var i = 0, len = array2.length; i < len; i++){
array1.push(array2[i]);
}
} }
function appendSet(set1, set2){ if(parsed_ean.type === 'price'){
for(key in set2){ selectedOrder.addProduct(new module.Product(product), {price:parsed_ean.value});
set1[key] = set2[key]; }else if(parsed_ean.type === 'weight'){
} selectedOrder.addProduct(new module.Product(product), {quantity:parsed_ean.value, merge:false});
}else{
selectedOrder.addProduct(new module.Product(product));
} }
return true;
var categories_by_id = {};
for(var i = 0; i < categories.length; i++){
categories_by_id[categories[i].id] = categories[i];
}
this.categories_by_id = categories_by_id;
var root_category = {
name : 'Root',
id : 0,
parent : null,
childrens : [],
};
// add parent and childrens field to categories, find root_categories
for(var i = 0; i < categories.length; i++){
var cat = categories[i];
cat.parent = categories_by_id[cat.parent_id[0]];
if(!cat.parent){
root_category.childrens.push(cat);
cat.parent = root_category;
}
cat.childrens = [];
for(var j = 0; j < cat.child_id.length; j++){
cat.childrens.push(categories_by_id[ cat.child_id[j] ]);
}
}
categories.push(root_category);
// set some default fields for next steps
for(var i = 0; i < categories.length; i++){
var cat = categories[i];
cat.product_list = []; //list of all products in the category
cat.product_set = {}; // [product.id] === true if product is in category
cat.weightable_product_list = [];
cat.weightable_product_set = {};
cat.weightable = false; //true if directly contains weightable products
}
this.root_category = root_category;
//we add the products to the categories.
for(var i = 0, len = products.length; i < len; i++){
var product = products[i];
var cat = categories_by_id[product.pos_categ_id[0]];
if(cat){
cat.product_list.push(product);
cat.product_set[product.id] = true;
if(product.to_weight){
cat.weightable_product_list.push(product);
cat.weightable_product_set[product.id] = true;
cat.weightable = true;
}
}
}
// we build a flat list of all categories that directly contains weightable products
this.weightable_categories = [];
for(var i = 0, len = categories.length; i < len; i++){
var cat = categories[i];
if(cat.weightable){
this.weightable_categories.push(cat);
}
}
// add ancestor field to categories, contains the list of parents of parents, from root to parent
function make_ancestors(cat, ancestors){
cat.ancestors = ancestors.slice(0);
ancestors.push(cat);
for(var i = 0; i < cat.childrens.length; i++){
make_ancestors(cat.childrens[i], ancestors.slice(0));
}
}
//add the products of the subcategories to the parent categories
function make_products(cat){
for(var i = 0; i < cat.childrens.length; i++){
make_products(cat.childrens[i]);
append(cat.product_list, cat.childrens[i].product_list);
append(cat.weightable_product_list, cat.childrens[i].weightable_product_list);
appendSet(cat.product_set, cat.childrens[i].product_set);
appendSet(cat.weightable_product_set, cat.childrens[i].weightable_product_set);
}
}
make_ancestors(root_category,[]);
make_products(root_category);
}, },
}); });
@ -608,15 +389,10 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
return false; return false;
}else if(this.get_discount() > 0){ // we don't merge discounted orderlines }else if(this.get_discount() > 0){ // we don't merge discounted orderlines
return false; return false;
}else if(this.get_product_type() === 'unit'){ }else if(this.price !== orderline.price){
return true;
}else if(this.get_product_type() === 'weight'){
return true;
}else if(this.get_product_type() === 'price'){
return this.get_product().get('list_price') === orderline.get_product().get('list_price');
}else{
console.error('point_of_sale/pos_models.js/Orderline.can_be_merged_with() : unknown product type:',this.get('product_type'));
return false; return false;
}else{
return true;
} }
}, },
merge: function(orderline){ merge: function(orderline){
@ -882,6 +658,7 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
change: this.getChange(), change: this.getChange(),
name : this.getName(), name : this.getName(),
client: client ? client.name : null , client: client ? client.name : null ,
invoice_id: null, //TODO
cashier: cashier ? cashier.name : null, cashier: cashier ? cashier.name : null,
date: { date: {
year: date.getFullYear(), year: date.getFullYear(),
@ -895,7 +672,7 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
email: company.email, email: company.email,
website: company.website, website: company.website,
company_registry: company.company_registry, company_registry: company.company_registry,
contact_address: null, //TODO contact_address: company.contact_address,
vat: company.vat, vat: company.vat,
name: company.name, name: company.name,
phone: company.phone, phone: company.phone,

View File

@ -152,14 +152,6 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
this.pos_widget.screen_selector.show_popup('help'); this.pos_widget.screen_selector.show_popup('help');
}, },
logout_button_action: function(){
this.pos_widget.screen_selector.set_user_mode('client');
},
close_button_action: function(){
this.pos_widget.try_close();
},
barcode_product_screen: 'products', //if defined, this screen will be loaded when a product is scanned barcode_product_screen: 'products', //if defined, this screen will be loaded when a product is scanned
barcode_product_error_popup: 'error', //if defined, this popup will be loaded when there's an error in the popup barcode_product_error_popup: 'error', //if defined, this popup will be loaded when there's an error in the popup
@ -167,14 +159,16 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
// it will add the product to the order and go to barcode_product_screen. Or show barcode_product_error_popup if // it will add the product to the order and go to barcode_product_screen. Or show barcode_product_error_popup if
// there's an error. // there's an error.
barcode_product_action: function(ean){ barcode_product_action: function(ean){
if(this.pos_widget.scan_product(ean)){ var self = this;
this.pos.proxy.scan_item_success(); if(self.pos.scan_product(ean)){
if(this.barcode_product_screen){ self.pos.proxy.scan_item_success(ean);
this.pos_widget.screen_selector.set_current_screen(this.barcode_product_screen); if(self.barcode_product_screen){
self.pos_widget.screen_selector.set_current_screen(self.barcode_product_screen);
} }
}else{ }else{
if(this.barcode_product_error_popup){ self.pos.proxy.scan_item_error_unrecognized(ean);
this.pos_widget.screen_selector.show_popup(this.barcode_product_error_popup); if(self.barcode_product_error_popup && self.pos_widget.screen_selector.get_user_mode() !== 'cashier'){
self.pos_widget.screen_selector.show_popup(self.barcode_product_error_popup);
} }
} }
}, },
@ -194,6 +188,7 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
return true; return true;
} }
} }
this.pos.proxy.scan_item_unrecognized(ean);
return false; return false;
}, },
@ -207,9 +202,11 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
if(users[i].ean13 === ean.ean){ if(users[i].ean13 === ean.ean){
this.pos.get('selectedOrder').set_client(users[i]); this.pos.get('selectedOrder').set_client(users[i]);
this.pos_widget.username.refresh(); this.pos_widget.username.refresh();
this.pos.proxy.scan_item_success(ean);
return true; return true;
} }
} }
this.pos.proxy.scan_item_unrecognized(ean);
return false; return false;
//TODO start the transaction //TODO start the transaction
}, },
@ -217,29 +214,78 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
// what happens when a discount barcode is scanned : the default behavior // what happens when a discount barcode is scanned : the default behavior
// is to set the discount on the last order. // is to set the discount on the last order.
barcode_discount_action: function(ean){ barcode_discount_action: function(ean){
this.pos.proxy.scan_item_success(ean);
var last_orderline = this.pos.get('selectedOrder').getLastOrderline(); var last_orderline = this.pos.get('selectedOrder').getLastOrderline();
if(last_orderline){ if(last_orderline){
last_orderline.set_discount(ean.value) last_orderline.set_discount(ean.value)
} }
}, },
// shows an action bar on the screen. The actionbar is automatically shown when you add a button
// with add_action_button()
show_action_bar: function(){
this.pos_widget.action_bar.show();
this.$element.css({'bottom':'105px'});
},
// hides the action bar. The actionbar is automatically hidden when it is empty
hide_action_bar: function(){
this.pos_widget.action_bar.hide();
this.$element.css({'bottom':'0px'});
},
// adds a new button to the action bar. The button definition takes three parameters, all optional :
// - label: the text below the button
// - icon: a small icon that will be shown
// - click: a callback that will be executed when the button is clicked.
// the method returns a reference to the button widget, and automatically show the actionbar.
add_action_button: function(button_def){
this.show_action_bar();
return this.pos_widget.action_bar.add_new_button(button_def);
},
// this method shows the screen and sets up all the widget related to this screen. Extend this method // this method shows the screen and sets up all the widget related to this screen. Extend this method
// if you want to alter the behavior of the screen. // if you want to alter the behavior of the screen.
show: function(){ show: function(){
var self = this;
this.hidden = false; this.hidden = false;
if(this.$element){ if(this.$element){
this.$element.show(); this.$element.show();
} }
if(this.pos_widget.action_bar.get_button_count() > 0){
this.show_action_bar();
}else{
this.hide_action_bar();
}
// we add the help button by default. we do this because the buttons are cleared on each refresh so that
// the button stay local to each screen
this.pos_widget.left_action_bar.add_new_button({
label: 'help',
icon: '/point_of_sale/static/src/img/icons/png48/help.png',
click: function(){ self.help_button_action(); },
});
var self = this; var self = this;
var cashier_mode = this.pos_widget.screen_selector.get_user_mode() === 'cashier'; var cashier_mode = this.pos_widget.screen_selector.get_user_mode() === 'cashier';
this.pos_widget.set_numpad_visible(this.show_numpad && cashier_mode); this.pos_widget.set_numpad_visible(this.show_numpad && cashier_mode);
this.pos_widget.set_leftpane_visible(this.show_leftpane); this.pos_widget.set_leftpane_visible(this.show_leftpane);
this.pos_widget.set_left_action_bar_visible(this.show_leftpane && !cashier_mode);
this.pos_widget.set_cashier_controls_visible(cashier_mode); this.pos_widget.set_cashier_controls_visible(cashier_mode);
this.pos_widget.action_bar.set_element_visible('help-button', !cashier_mode, function(){ self.help_button_action(); });
this.pos_widget.action_bar.set_element_visible('logout-button', cashier_mode && this.pos.use_selfcheckout, function(){ self.logout_button_action(); }); if(cashier_mode && this.pos.iface_self_checkout){
this.pos_widget.action_bar.set_element_visible('close-button', cashier_mode, function(){ self.close_button_action(); }); this.pos_widget.client_button.show();
}else{
this.pos_widget.client_button.hide();
}
if(cashier_mode){
this.pos_widget.close_button.show();
}else{
this.pos_widget.close_button.hide();
}
this.pos_widget.username.set_user_mode(this.pos_widget.screen_selector.get_user_mode()); this.pos_widget.username.set_user_mode(this.pos_widget.screen_selector.get_user_mode());
@ -251,16 +297,14 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
}); });
}, },
// this method is called when the screen is closed to make place for a new screen. this is a good place // this method is called when the screen is closed to make place for a new screen. this is a good place
// to put your cleanup stuff as it is guaranteed that for each show() there is one and only one close() // to put your cleanup stuff as it is guaranteed that for each show() there is one and only one close()
close: function(){ close: function(){
if(this.pos.barcode_reader){ if(this.pos.barcode_reader){
this.pos.barcode_reader.reset_action_callbacks(); this.pos.barcode_reader.reset_action_callbacks();
} }
if(this.pos_widget.action_bar){ this.pos_widget.action_bar.destroy_buttons();
this.pos_widget.action_bar.destroy_buttons(); this.pos_widget.left_action_bar.destroy_buttons();
}
}, },
// this methods hides the screen. It's not a good place to put your cleanup stuff as it is called on the // this methods hides the screen. It's not a good place to put your cleanup stuff as it is called on the
@ -349,6 +393,9 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
module.ScaleInviteScreenWidget = module.ScreenWidget.extend({ module.ScaleInviteScreenWidget = module.ScreenWidget.extend({
template:'ScaleInviteScreenWidget', template:'ScaleInviteScreenWidget',
next_screen:'scale',
previous_screen:'products',
show: function(){ show: function(){
this._super(); this._super();
var self = this; var self = this;
@ -359,21 +406,19 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
var weight = self.pos.proxy.weighting_read_kg(); var weight = self.pos.proxy.weighting_read_kg();
if(weight > 0.001){ if(weight > 0.001){
clearInterval(this.intervalID); clearInterval(this.intervalID);
self.pos_widget.screen_selector.set_current_screen('scale'); self.pos_widget.screen_selector.set_current_screen(self.next_screen);
} }
},500); },500);
this.pos_widget.action_bar.add_new_button( this.add_action_button({
{
label: 'back', label: 'back',
icon: '/point_of_sale/static/src/img/icons/png48/go-previous.png', icon: '/point_of_sale/static/src/img/icons/png48/go-previous.png',
click: function(){ click: function(){
clearInterval(this.intervalID); clearInterval(this.intervalID);
self.pos.proxy.weighting_end(); self.pos.proxy.weighting_end();
self.pos_widget.screen_selector.set_current_screen('products'); self.pos_widget.screen_selector.set_current_screen(self.previous_screen);
} }
} });
);
}, },
close: function(){ close: function(){
this._super(); this._super();
@ -383,25 +428,30 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
module.ScaleScreenWidget = module.ScreenWidget.extend({ module.ScaleScreenWidget = module.ScreenWidget.extend({
template:'ScaleScreenWidget', template:'ScaleScreenWidget',
next_screen: 'products',
previous_screen: 'products',
show: function(){ show: function(){
this._super(); this._super();
this.renderElement(); this.renderElement();
var self = this; var self = this;
this.pos_widget.action_bar.add_new_button({
this.add_action_button({
label: 'back', label: 'back',
icon: '/point_of_sale/static/src/img/icons/png48/go-previous.png', icon: '/point_of_sale/static/src/img/icons/png48/go-previous.png',
click: function(){ click: function(){
self.pos_widget.screen_selector.set_current_screen('products'); self.pos_widget.screen_selector.set_current_screen(self.previous_screen);
} }
}); });
this.validate_button = this.pos_widget.action_bar.add_new_button({ this.validate_button = this.add_action_button({
label: 'Validate', label: 'Validate',
icon: '/point_of_sale/static/src/img/icons/png48/validate.png', icon: '/point_of_sale/static/src/img/icons/png48/validate.png',
click: function(){ click: function(){
self.order_product(); self.order_product();
self.pos_widget.screen_selector.set_current_screen('products'); self.pos_widget.screen_selector.set_current_screen(self.next_screen);
}, },
}); });
@ -419,7 +469,7 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
this._super(); this._super();
this.$('.product-picture').click(function(){ this.$('.product-picture').click(function(){
self.order_product(); self.order_product();
self.pos_widget.screen_selector.set_current_screen('products'); self.pos_widget.screen_selector.set_current_screen(self.next_screen);
}); });
}, },
get_product: function(){ get_product: function(){
@ -444,7 +494,7 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
}, },
get_product_image: function(){ get_product_image: function(){
var product = this.get_product(); var product = this.get_product();
return product ? product.get('image_medium') : undefined; return product ? product.get('image') : undefined;
}, },
get_product_weight: function(){ get_product_weight: function(){
return this.weight || 0; return this.weight || 0;
@ -458,6 +508,10 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
module.ClientPaymentScreenWidget = module.ScreenWidget.extend({ module.ClientPaymentScreenWidget = module.ScreenWidget.extend({
template:'ClientPaymentScreenWidget', template:'ClientPaymentScreenWidget',
next_screen: 'welcome',
previous_screen: 'products',
show: function(){ show: function(){
this._super(); this._super();
var self = this; var self = this;
@ -486,7 +540,7 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
self.pos.push_order(currentOrder.exportAsJSON()).then(function() { self.pos.push_order(currentOrder.exportAsJSON()).then(function() {
currentOrder.destroy(); currentOrder.destroy();
self.pos.proxy.transaction_end(); self.pos.proxy.transaction_end();
self.pos_widget.screen_selector.set_current_screen('welcome'); self.pos_widget.screen_selector.set_current_screen(self.next_screen);
}); });
}else if(payment === 'payment_rejected'){ }else if(payment === 'payment_rejected'){
clearInterval(this.intervalID); clearInterval(this.intervalID);
@ -494,17 +548,15 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
} }
},500); },500);
this.pos_widget.action_bar.add_new_button( this.add_action_button({
{
label: 'back', label: 'back',
icon: '/point_of_sale/static/src/img/icons/png48/go-previous.png', icon: '/point_of_sale/static/src/img/icons/png48/go-previous.png',
click: function(){ //TODO Go to ask for weighting screen click: function(){
clearInterval(this.intervalID); clearInterval(this.intervalID);
self.pos.proxy.payment_canceled(); self.pos.proxy.payment_canceled();
self.pos_widget.screen_selector.set_current_screen('products'); self.pos_widget.screen_selector.set_current_screen(self.previous_screen);
} }
} });
);
}, },
close: function(){ close: function(){
this._super(); this._super();
@ -515,23 +567,32 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
module.WelcomeScreenWidget = module.ScreenWidget.extend({ module.WelcomeScreenWidget = module.ScreenWidget.extend({
template:'WelcomeScreenWidget', template:'WelcomeScreenWidget',
next_screen: 'products',
show_numpad: false, show_numpad: false,
show_leftpane: false, show_leftpane: false,
barcode_client_action: function(ean){ barcode_client_action: function(ean){
this._super(ean); this._super(ean);
this.pos_widget.screen_selector.set_current_screen('products'); this.pos_widget.screen_selector.set_current_screen(this.next_screen);
}, },
show: function(){ show: function(){
this._super(); this._super();
var self = this; var self = this;
$('.goodbye-message').css({opacity:1}).show();
setTimeout(function(){
$('.goodbye-message').animate({opacity:0},500,'swing',function(){$('.goodbye-message').hide();});
},3000);
}, },
}); });
module.ProductScreenWidget = module.ScreenWidget.extend({ module.ProductScreenWidget = module.ScreenWidget.extend({
template:'ProductScreenWidget', template:'ProductScreenWidget',
scale_screen: 'scale_invite',
client_next_screen: 'client_payment',
show_numpad: true, show_numpad: true,
show_leftpane: true, show_leftpane: true,
@ -542,8 +603,8 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
this.product_list_widget = new module.ProductListWidget(this,{ this.product_list_widget = new module.ProductListWidget(this,{
click_product_action: function(product){ click_product_action: function(product){
if(product.get('to_weight') && self.pos.use_scale){ if(product.get('to_weight') && self.pos.iface_electronic_scale){
self.pos_widget.screen_selector.set_current_screen('scale_invite', {product: product}); self.pos_widget.screen_selector.set_current_screen(self.scale_screen, {product: product});
}else{ }else{
self.pos.get('selectedOrder').addProduct(product); self.pos.get('selectedOrder').addProduct(product);
} }
@ -559,16 +620,16 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
this.product_categories_widget.reset_category(); this.product_categories_widget.reset_category();
this.pos_widget.order_widget.set_numpad_state(this.pos_widget.numpad.state); this.pos_widget.order_widget.set_numpad_state(this.pos_widget.numpad.state);
if(this.pos.use_virtual_keyboard){ if(this.pos.iface_vkeyboard){
this.pos_widget.onscreen_keyboard.connect(); this.pos_widget.onscreen_keyboard.connect();
} }
if(this.pos_widget.screen_selector.current_mode === 'client'){ if(this.pos_widget.screen_selector.current_mode === 'client'){
this.pos_widget.action_bar.add_new_button({ this.add_action_button({
label: 'pay', label: 'pay',
icon: '/point_of_sale/static/src/img/icons/png48/go-next.png', icon: '/point_of_sale/static/src/img/icons/png48/go-next.png',
click: function(){ click: function(){
self.pos_widget.screen_selector.set_current_screen('client_payment'); self.pos_widget.screen_selector.set_current_screen(self.client_next_screen);
} }
}); });
} }
@ -604,17 +665,19 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
this._super(); this._super();
var self = this; var self = this;
this.pos_widget.action_bar.add_new_button({ this.add_action_button({
label: 'Print', label: 'Print',
icon: '/point_of_sale/static/src/img/icons/png48/printer.png', icon: '/point_of_sale/static/src/img/icons/png48/printer.png',
click: function(){ self.print(); }, click: function(){ self.print(); },
}); });
this.pos_widget.action_bar.add_new_button({ this.add_action_button({
label: 'Next Order', label: 'Next Order',
icon: '/point_of_sale/static/src/img/icons/png48/go-next.png', icon: '/point_of_sale/static/src/img/icons/png48/go-next.png',
click: function() { self.finishOrder(); }, click: function() { self.finishOrder(); },
}); });
window.print();
}, },
print: function() { print: function() {
window.print(); window.print();
@ -643,6 +706,8 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
module.PaymentScreenWidget = module.ScreenWidget.extend({ module.PaymentScreenWidget = module.ScreenWidget.extend({
template: 'PaymentScreenWidget', template: 'PaymentScreenWidget',
back_screen: 'products',
next_screen: 'receipt',
init: function(parent, options) { init: function(parent, options) {
this._super(parent,options); this._super(parent,options);
this.model = options.model; this.model = options.model;
@ -654,21 +719,21 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
this._super(); this._super();
var self = this; var self = this;
if(this.pos.use_cashbox){ if(this.pos.iface_cashdrawer){
this.pos.proxy.open_cashbox(); this.pos.proxy.open_cashbox();
} }
this.set_numpad_state(this.pos_widget.numpad.state); this.set_numpad_state(this.pos_widget.numpad.state);
this.back_button = this.pos_widget.action_bar.add_new_button({ this.back_button = this.add_action_button({
label: 'Back', label: 'Back',
icon: '/point_of_sale/static/src/img/icons/png48/go-previous.png', icon: '/point_of_sale/static/src/img/icons/png48/go-previous.png',
click: function(){ click: function(){
self.pos_widget.screen_selector.set_current_screen('products'); self.pos_widget.screen_selector.set_current_screen(self.back_screen);
}, },
}); });
this.validate_button = this.pos_widget.action_bar.add_new_button({ this.validate_button = this.add_action_button({
label: 'Validate', label: 'Validate',
icon: '/point_of_sale/static/src/img/icons/png48/validate.png', icon: '/point_of_sale/static/src/img/icons/png48/validate.png',
click: function(){ click: function(){
@ -682,24 +747,18 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
this.pos_widget.payment_screen.set_numpad_state(null); this.pos_widget.payment_screen.set_numpad_state(null);
}, },
back: function() { back: function() {
this.pos_widget.screen_selector.set_current_screen('products'); this.pos_widget.screen_selector.set_current_screen(self.back_screen);
}, },
validateCurrentOrder: function() { validateCurrentOrder: function() {
var self = this;
var currentOrder = this.pos.get('selectedOrder'); var currentOrder = this.pos.get('selectedOrder');
this.validate_button.$element.attr('disabled','disabled'); //FIXME is the css actually using this attr ?
this.pos.push_order(currentOrder.exportAsJSON()) this.pos.push_order(currentOrder.exportAsJSON())
.then(function() { if(this.pos.iface_print_via_proxy){
self.validate_button.$element.removeAttr('disabled'); this.pos.proxy.print_receipt(currentOrder.export_for_printing());
if(self.pos.use_proxy_printer){ this.pos.get('selectedOrder').destroy(); //finish order and go back to scan screen
self.pos.proxy.print_receipt(currentOrder.export_for_printing()); }else{
self.pos.get('selectedOrder').destroy(); //finish order and go back to scan screen this.pos_widget.screen_selector.set_current_screen(this.next_screen);
}else{ }
self.pos_widget.screen_selector.set_current_screen('receipt');
}
});
}, },
bindPaymentLineEvents: function() { bindPaymentLineEvents: function() {
this.currentPaymentLines = (this.pos.get('selectedOrder')).get('paymentLines'); this.currentPaymentLines = (this.pos.get('selectedOrder')).get('paymentLines');

View File

@ -65,6 +65,7 @@ function openerp_pos_scrollbar(instance, module){ //module is instance.point_of_
this.auto_hide(false); this.auto_hide(false);
this.$element.bind('mousewheel',function(event,delta){ this.$element.bind('mousewheel',function(event,delta){
self.scroll(delta*self.wheel_step); self.scroll(delta*self.wheel_step);
return false;
}); });
this.$element.bind('click',function(event){ this.$element.bind('click',function(event){
var vpos = event.pageY - self.$element.offset().top; var vpos = event.pageY - self.$element.offset().top;

View File

@ -1,6 +1,57 @@
function openerp_pos_widgets(instance, module){ //module is instance.point_of_sale function openerp_pos_widgets(instance, module){ //module is instance.point_of_sale
var QWeb = instance.web.qweb; var QWeb = instance.web.qweb;
// The ImageCache is used to hide the latency of the application cache on-disk access in chrome
// that causes annoying flickering on product pictures. Why the hell a simple access to
// the application cache involves such latency is beyond me, hopefully one day this can be
// removed.
module.ImageCache = instance.web.Class.extend({
init: function(options){
options = options || {};
this.max_size = options.max_size || 500;
this.cache = {};
this.access_time = {};
this.size = 0;
},
get_image_uncached: function(url){
var img = new Image();
img.src = url;
return img;
},
// returns a DOM Image object from an url, and cache the last 500 (by default) results
get_image: function(url){
var cached = this.cache[url];
if(cached){
this.access_time[url] = (new Date()).getTime();
return cached;
}else{
var img = new Image();
img.src = url;
while(this.size >= this.max_size){
var oldestUrl = null;
var oldestTime = (new Date()).getTime();
for(var url in this.cache){
var time = this.access_time[url];
if(time <= oldestTime){
oldestTime = time;
oldestUrl = url;
}
}
if(oldestUrl){
delete this.cache[oldestUrl];
delete this.access_time[oldestUrl];
}
this.size--;
}
this.cache[url] = img;
this.access_time[url] = (new Date()).getTime();
this.size++;
return img;
}
},
});
module.NumpadWidget = module.PosBaseWidget.extend({ module.NumpadWidget = module.PosBaseWidget.extend({
template:'NumpadWidget', template:'NumpadWidget',
init: function(parent, options) { init: function(parent, options) {
@ -112,7 +163,7 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
template:'OrderWidget', template:'OrderWidget',
init: function(parent, options) { init: function(parent, options) {
this._super(parent,options); this._super(parent,options);
this.compact = false; this.display_mode = options.display_mode || 'numpad'; // 'maximized' | 'actionbar' | 'numpad'
this.set_numpad_state(options.numpadState); this.set_numpad_state(options.numpadState);
this.pos.bind('change:selectedOrder', this.change_selected_order, this); this.pos.bind('change:selectedOrder', this.change_selected_order, this);
this.bind_orderline_events(); this.bind_orderline_events();
@ -165,8 +216,12 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
var self = this; var self = this;
this._super(); this._super();
if(!this.compact){ if(this.display_mode === 'maximized'){
$('.point-of-sale .order-container').css({'bottom':'0px'}); $('.point-of-sale .order-container').css({'bottom':'0px'});
}else if(this.display_mode === 'actionbar'){
$('.point-of-sale .order-container').css({'bottom':'105px'});
}else if(this.display_mode !== 'numpad'){
console.error('ERROR: OrderWidget renderElement(): wrong display_mode:',this.display_mode);
} }
var $content = this.$('.orderlines'); var $content = this.$('.orderlines');
@ -211,9 +266,9 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
var total = order ? order.getTotal() : 0; var total = order ? order.getTotal() : 0;
this.$('.summary .value.total').html(this.format_currency(total)); this.$('.summary .value.total').html(this.format_currency(total));
}, },
set_compact: function(compact){ set_display_mode: function(mode){
if(this.compact !== compact){ if(this.display_mode !== mode){
this.compact = compact; this.display_mode = mode;
this.renderElement(); this.renderElement();
} }
}, },
@ -225,20 +280,16 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
this._super(parent,options); this._super(parent,options);
this.model = options.model; this.model = options.model;
this.model.attributes.weight = options.weight; this.model.attributes.weight = options.weight;
this.next_screen = options.next_screen; this.next_screen = options.next_screen; //when a product is clicked, this screen is set
this.click_product_action = options.click_product_action; this.click_product_action = options.click_product_action;
}, },
add_to_order: function(event) { // returns the url of the product thumbnail
/* Preserve the category URL */ get_image_url: function() {
event.preventDefault(); return '/web/binary/image?session_id='+instance.session.session_id+'&model=product.product&field=image&id='+this.model.get('id');
return (this.pos.get('selectedOrder')).addProduct(this.model);
},
set_weight: function(weight){
this.model.attributes.weight = weight;
this.renderElement();
}, },
renderElement: function() { renderElement: function() {
this._super(); this._super();
this.$('img').replaceWith(this.pos_widget.image_cache.get_image(this.get_image_url()));
var self = this; var self = this;
$("a", this.$element).click(function(e){ $("a", this.$element).click(function(e){
if(self.click_product_action){ if(self.click_product_action){
@ -313,6 +364,7 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
module.ActionButtonWidget = instance.web.Widget.extend({ module.ActionButtonWidget = instance.web.Widget.extend({
template:'ActionButtonWidget', template:'ActionButtonWidget',
icon_template:'ActionButtonWidgetWithIcon',
init: function(parent, options){ init: function(parent, options){
this._super(parent, options); this._super(parent, options);
this.label = options.label || 'button'; this.label = options.label || 'button';
@ -320,7 +372,7 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
this.click_action = options.click; this.click_action = options.click;
if(options.icon){ if(options.icon){
this.icon = options.icon; this.icon = options.icon;
this.template = 'ActionButtonWidgetWithIcon'; this.template = this.icon_template;
} }
}, },
renderElement: function(){ renderElement: function(){
@ -359,21 +411,25 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
this.button_list = []; this.button_list = [];
return this; return this;
}, },
get_button_count: function(){
return this.button_list.length;
},
add_new_button: function(button_options){ add_new_button: function(button_options){
if(arguments.length == 1){ var button = new module.ActionButtonWidget(this,button_options);
var button = new module.ActionButtonWidget(this,button_options); this.button_list.push(button);
this.button_list.push(button); button.appendTo(this.$('.pos-actionbar-button-list'));
button.appendTo($('.pos-actionbar-button-list')); return button;
return button; },
}else{ show:function(){
for(var i = 0; i < arguments.length; i++){ this.$element.show();
this.add_new_button(arguments[i]); },
} hide:function(){
} this.$element.hide();
return undefined;
}, },
}); });
module.CategoryButton = module.PosBaseWidget.extend({
});
module.ProductCategoriesWidget = module.PosBaseWidget.extend({ module.ProductCategoriesWidget = module.PosBaseWidget.extend({
template: 'ProductCategoriesWidget', template: 'ProductCategoriesWidget',
init: function(parent, options){ init: function(parent, options){
@ -389,41 +445,53 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
// changes the category. if undefined, sets to root category // changes the category. if undefined, sets to root category
set_category : function(category){ set_category : function(category){
var db = this.pos.db;
if(!category){ if(!category){
this.category = this.pos.root_category; this.category = db.get_category_by_id(db.root_category_id);
}else{ }else{
this.category = category; this.category = category;
} }
this.breadcrumb = []; this.breadcrumb = [];
for(var i = 1; i < this.category.ancestors.length; i++){ var ancestors_ids = db.get_category_ancestors_ids(this.category.id);
this.breadcrumb.push(this.category.ancestors[i]); for(var i = 1; i < ancestors_ids.length; i++){
this.breadcrumb.push(db.get_category_by_id(ancestors_ids[i]));
} }
if(this.category !== this.pos.root_category){ if(this.category.id !== db.root_category_id){
this.breadcrumb.push(this.category); this.breadcrumb.push(this.category);
} }
if(this.product_type === 'weightable'){ this.subcategories = db.get_category_by_id(db.get_category_childs_ids(this.category.id));
this.subcategories = []; },
for(var i = 0; i < this.category.childrens.length; i++){
if(this.category.childrens[i].weightable_product_list.length > 0){ get_image_url: function(category){
this.subcategories.push( this.category.childrens[i]); return '/web/binary/image?session_id='+instance.session.session_id+'&model=pos.category&field=image&id='+category.id;
}
}
}else{
this.subcategories = this.category.childrens || [];
}
}, },
renderElement: function(){ renderElement: function(){
var self = this; var self = this;
this._super(); this._super();
_.each(this.subcategories, function(category){
var button = QWeb.render('CategoryButton',{category:category});
button = _.str.trim(button);
$(button).appendTo(this.$('.category-list')).click(function(event){ var hasimages = false; //if none of the subcategories have images, we don't display buttons with icons
_.each(this.subcategories, function(category){
if(category.image){
hasimages = true;
}
});
_.each(this.subcategories, function(category){
if(hasimages){
var button = QWeb.render('CategoryButton',{category:category});
var button = _.str.trim(button);
var button = $(button);
button.find('img').replaceWith(self.pos_widget.image_cache.get_image(self.get_image_url(category)));
}else{
var button = QWeb.render('CategorySimpleButton',{category:category});
button = _.str.trim(button); // we remove whitespace between buttons to fix spacing
var button = $(button);
}
button.appendTo(this.$('.category-list')).click(function(event){
var id = category.id; var id = category.id;
var cat = self.pos.categories_by_id[id]; var cat = self.pos.db.get_category_by_id(id);
self.set_category(cat); self.set_category(cat);
self.renderElement(); self.renderElement();
self.search_and_categories(cat); self.search_and_categories(cat);
@ -432,7 +500,7 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
// breadcrumb click actions // breadcrumb click actions
this.$(".oe-pos-categories-list a").click(function(event){ this.$(".oe-pos-categories-list a").click(function(event){
var id = $(event.target).data("category-id"); var id = $(event.target).data("category-id");
var category = self.pos.categories_by_id[id]; var category = self.pos.db.get_category_by_id(id);
self.set_category(category); self.set_category(category);
self.renderElement(); self.renderElement();
self.search_and_categories(category); self.search_and_categories(category);
@ -455,47 +523,30 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
// filters the products, and sets up the search callbacks // filters the products, and sets up the search callbacks
search_and_categories: function(category){ search_and_categories: function(category){
var self = this; var self = this;
var all_products = this.pos.get('product_list');
var all_packages = this.pos.get('product.packaging');
// find all products belonging to the current category // find all products belonging to the current category
var products = []; var products = this.pos.db.get_product_by_category(this.category.id);
if(this.product_type === 'weightable'){ self.pos.get('products').reset(products);
products = all_products.filter( function(product){
return self.category.weightable_product_set[product.id];
});
}else{
products = all_products.filter( function(product){
return self.category.product_set[product.id];
});
}
// product lists watch for reset events on 'products' to re-render. // filter the products according to the search string
// FIXME that means all productlist widget re-render... even the hidden ones !
this.pos.get('products').reset(products);
// find all the products whose name match the query in the searchbox
this.$('.searchbox input').keyup(function(){ this.$('.searchbox input').keyup(function(){
var results, search_str; query = $(this).val().toLowerCase();
search_str = $(this).val().toLowerCase(); if(query){
if(search_str){ var products = self.pos.db.search_product_in_category(self.category.id, query);
results = products.filter( function(p){ self.pos.get('products').reset(products);
return p.name.toLowerCase().indexOf(search_str) != -1 || self.$('.search-clear').fadeIn();
(p.ean13 && p.ean13.indexOf(search_str) != -1);
});
self.$element.find('.search-clear').fadeIn();
}else{ }else{
results = products; var products = self.pos.db.get_product_by_category(self.category.id);
self.$element.find('.search-clear').fadeOut(); self.pos.get('products').reset(products);
self.$('.search-clear').fadeOut();
} }
self.pos.get('products').reset(results);
});
this.$('.searchbox input').click(function(){
}); });
this.$('.searchbox input').click(function(){}); //Why ???
//reset the search when clicking on reset //reset the search when clicking on reset
this.$('.search-clear').click(function(){ this.$('.search-clear').click(function(){
var products = self.pos.db.get_product_by_category(self.category.id);
self.pos.get('products').reset(products); self.pos.get('products').reset(products);
self.$('.searchbox input').val('').focus(); self.$('.searchbox input').val('').focus();
self.$('.search-clear').fadeOut(); self.$('.search-clear').fadeOut();
@ -519,11 +570,6 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
self.renderElement(); self.renderElement();
}); });
}, },
set_weight: function(weight){
for(var i = 0; i < this.product_list.length; i++){
this.product_list[i].set_weight(weight);
}
},
renderElement: function() { renderElement: function() {
var self = this; var self = this;
this._super(); this._super();
@ -586,14 +632,35 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
}, },
}); });
module.HeaderButtonWidget = module.PosBaseWidget.extend({
template: 'HeaderButtonWidget',
init: function(parent, options){
options = options || {};
this._super(parent, options);
this.action = options.action;
this.label = options.label;
},
renderElement: function(){
var self = this;
this._super();
if(this.action){
this.$element.click(function(){ self.action(); });
}
},
show: function(){ this.$element.show(); },
hide: function(){ this.$element.hide(); },
});
// ---------- Main Point of Sale Widget ---------- // ---------- Main Point of Sale Widget ----------
// this is used to notify the user that data is being synchronized on the network // this is used to notify the user that data is being synchronized on the network
module.SynchNotificationWidget = module.PosBaseWidget.extend({ module.SynchNotificationWidget = module.PosBaseWidget.extend({
template: "SynchNotificationWidget", template: "SynchNotificationWidget",
init: function(parent,options) { init: function(parent, options){
options = options || {}; options = options || {};
this._super(parent,options); this._super(parent, options);
}, },
renderElement: function() { renderElement: function() {
var self = this; var self = this;
@ -632,9 +699,19 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
this.pos_widget = this; //So that pos_widget's childs have pos_widget set automatically this.pos_widget = this; //So that pos_widget's childs have pos_widget set automatically
this.numpad_visible = true; this.numpad_visible = true;
this.left_action_bar_visible = true;
this.leftpane_visible = true; this.leftpane_visible = true;
this.leftpane_width = '440px'; this.leftpane_width = '440px';
this.cashier_controls_visible = true; this.cashier_controls_visible = true;
this.image_cache = new module.ImageCache(); // for faster products image display
/*
//Epileptic mode
setInterval(function(){
$('body').css({'-webkit-filter':'hue-rotate('+Math.random()*360+'deg)' });
},100);
*/
}, },
start: function() { start: function() {
@ -643,7 +720,9 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
self.build_currency_template(); self.build_currency_template();
self.renderElement(); self.renderElement();
self.$('.neworder-button').click(_.bind(self.create_new_order, self)); self.$('.neworder-button').click(function(){
self.pos.add_new_order();
});
//when a new order is created, add an order button widget //when a new order is created, add an order button widget
self.pos.get('orders').bind('add', function(new_order){ self.pos.get('orders').bind('add', function(new_order){
@ -659,6 +738,10 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
self.build_widgets(); self.build_widgets();
self.screen_selector.set_default_screen();
self.pos.barcode_reader.connect();
instance.webclient.set_content_full_screen(true); instance.webclient.set_content_full_screen(true);
if (!self.pos.get('pos_session')) { if (!self.pos.get('pos_session')) {
@ -667,9 +750,14 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
self.screen_selector.show_popup('error', 'Sorry, we could not find any PoS Configuration for this session'); self.screen_selector.show_popup('error', 'Sorry, we could not find any PoS Configuration for this session');
} }
self.$('.loader').animate({opacity:0},3000,'swing',function(){$('.loader').hide();}); self.$('.loader').animate({opacity:0},1500,'swing',function(){self.$('.loader').hide();});
self.$('.loader img').hide(); self.$('.loader img').hide();
if(jQuery.deparam(jQuery.param.querystring()).debug !== undefined){
window.pos = self.pos;
window.pos_widget = self.pos_widget;
}
},function(){ // error when loading models data from the backend },function(){ // error when loading models data from the backend
self.$('.loader img').hide(); self.$('.loader img').hide();
return new instance.web.Model("ir.model.data").get_func("search_read")([['name', '=', 'action_pos_session_opening']], ['res_id']) return new instance.web.Model("ir.model.data").get_func("search_read")([['name', '=', 'action_pos_session_opening']], ['res_id'])
@ -682,8 +770,11 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
}, self)); }, self));
}); });
}, },
// This method instantiates all the screens, widgets, etc. If you want to add new screens change the
// startup screen, etc, override this method.
build_widgets: function() { build_widgets: function() {
var self = this;
// -------- Screens --------- // -------- Screens ---------
@ -725,13 +816,16 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
// -------- Misc --------- // -------- Misc ---------
this.notification = new module.SynchNotificationWidget(this,{}); this.notification = new module.SynchNotificationWidget(this,{});
this.notification.replace(this.$('.placeholder-SynchNotificationWidget')); this.notification.appendTo(this.$('#rightheader'));
this.username = new module.UsernameWidget(this,{}); this.username = new module.UsernameWidget(this,{});
this.username.replace(this.$('.placeholder-UsernameWidget')); this.username.replace(this.$('.placeholder-UsernameWidget'));
this.action_bar = new module.ActionBarWidget(this); this.action_bar = new module.ActionBarWidget(this);
this.action_bar.appendTo($(".point-of-sale #content")); this.action_bar.appendTo($(".point-of-sale #rightpane"));
this.left_action_bar = new module.ActionBarWidget(this);
this.left_action_bar.appendTo($(".point-of-sale #leftpane"));
this.paypad = new module.PaypadWidget(this, {}); this.paypad = new module.PaypadWidget(this, {});
this.paypad.replace($('#placeholder-PaypadWidget')); this.paypad.replace($('#placeholder-PaypadWidget'));
@ -746,6 +840,19 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
'keyboard_model': 'simple' 'keyboard_model': 'simple'
}); });
this.onscreen_keyboard.appendTo($(".point-of-sale #content")); this.onscreen_keyboard.appendTo($(".point-of-sale #content"));
this.close_button = new module.HeaderButtonWidget(this,{
label:'Close',
action: function(){ self.try_close(); },
});
this.close_button.appendTo(this.$('#rightheader'));
this.client_button = new module.HeaderButtonWidget(this,{
label:'Self-Checkout',
action: function(){ self.screen_selector.set_user_mode('client'); },
});
this.client_button.appendTo(this.$('#rightheader'));
// -------- Screen Selector --------- // -------- Screen Selector ---------
@ -768,59 +875,11 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
}, },
default_client_screen: 'welcome', default_client_screen: 'welcome',
default_cashier_screen: 'products', default_cashier_screen: 'products',
default_mode: this.pos.use_selfcheckout ? 'client' : 'cashier', default_mode: this.pos.iface_self_checkout ? 'client' : 'cashier',
}); });
this.screen_selector.set_default_screen();
this.pos.barcode_reader.connect();
}, },
//FIXME this method is probably not at the right place ...
scan_product: function(parsed_ean){
var selectedOrder = this.pos.get('selectedOrder');
var scannedProductModel = this.get_product_by_ean(parsed_ean);
if (!scannedProductModel){
return false;
} else {
if(parsed_ean.type === 'price'){
selectedOrder.addProduct(new module.Product(scannedProductModel), { price:parsed_ean.value});
}else if(parsed_ean.type === 'weight'){
selectedOrder.addProduct(new module.Product(scannedProductModel), { quantity:parsed_ean.value, merge:false});
}else{
selectedOrder.addProduct(new module.Product(scannedProductModel));
}
return true;
}
},
get_product_by_ean: function(parsed_ean) {
var allProducts = this.pos.get('product_list');
var allPackages = this.pos.get('product.packaging');
var scannedProductModel = undefined;
if (parsed_ean.type === 'price' || parsed_ean.type === 'weight') {
var itemCode = parsed_ean.id;
var scannedPackaging = _.detect(allPackages, function(pack) {
return pack.ean && pack.ean.substring(0,7) === itemCode;
});
if (scannedPackaging) {
scannedProductModel = _.detect(allProducts, function(pc) { return pc.id === scannedPackaging.product_id[0];});
}else{
scannedProductModel = _.detect(allProducts, function(pc) { return pc.ean13 && (pc.ean13.substring(0,7) === parsed_ean.id);});
}
} else if(parsed_ean.type === 'unit'){
scannedProductModel = _.detect(allProducts, function(pc) { return pc.ean13 === parsed_ean.ean;}); //TODO DOES NOT SCALE
}
return scannedProductModel;
},
// creates a new order, and add it to the list of orders.
create_new_order: function() {
var new_order;
new_order = new module.Order({ pos: this.pos });
this.pos.get('orders').add(new_order);
this.pos.set({ selectedOrder: new_order });
},
changed_pending_operations: function () { changed_pending_operations: function () {
var self = this; var self = this;
this.synch_notification.on_change_nbr_pending(self.pos.get('nbr_pending_operations').length); this.synch_notification.on_change_nbr_pending(self.pos.get('nbr_pending_operations').length);
@ -830,16 +889,35 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
if(visible !== this.numpad_visible){ if(visible !== this.numpad_visible){
this.numpad_visible = visible; this.numpad_visible = visible;
if(visible){ if(visible){
this.set_left_action_bar_visible(false);
this.numpad.show(); this.numpad.show();
this.paypad.show(); this.paypad.show();
this.order_widget.set_compact(true); this.order_widget.set_display_mode('numpad');
}else{ }else{
this.numpad.hide(); this.numpad.hide();
this.paypad.hide(); this.paypad.hide();
this.order_widget.set_compact(false); if(this.order_widget.display_mode === 'numpad'){
this.order_widget.set_display_mode('maximized');
}
} }
} }
}, },
set_left_action_bar_visible: function(visible){
if(visible !== this.left_action_bar_visible){
this.left_action_bar_visible = visible;
if(visible){
this.set_numpad_visible(false);
this.left_action_bar.show();
this.order_widget.set_display_mode('actionbar');
}else{
this.left_action_bar.hide();
if(this.order_widget.display_mode === 'actionbar'){
this.order_widget.set_display_mode('maximized');
}
}
}
},
//shows or hide the leftpane (contains the list of orderlines, the numpad, the paypad, etc.) //shows or hide the leftpane (contains the list of orderlines, the numpad, the paypad, etc.)
set_leftpane_visible: function(visible){ set_leftpane_visible: function(visible){
if(visible !== this.leftpane_visible){ if(visible !== this.leftpane_visible){
@ -874,12 +952,14 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
}); });
}, },
close: function() { close: function() {
var self = this;
this.pos.barcode_reader.disconnect(); this.pos.barcode_reader.disconnect();
return new instance.web.Model("ir.model.data").get_func("search_read")([['name', '=', 'action_client_pos_menu']], ['res_id']).pipe( return new instance.web.Model("ir.model.data").get_func("search_read")([['name', '=', 'action_client_pos_menu']], ['res_id']).pipe(
_.bind(function(res) { _.bind(function(res) {
return this.rpc('/web/action/load', {'action_id': res[0]['res_id']}).pipe(_.bind(function(result) { return this.rpc('/web/action/load', {'action_id': res[0]['res_id']}).pipe(_.bind(function(result) {
var action = result.result; var action = result.result;
action.context = _.extend(action.context || {}, {'cancel_action': {type: 'ir.actions.client', tag: 'reload'}}); action.context = _.extend(action.context || {}, {'cancel_action': {type: 'ir.actions.client', tag: 'reload'}});
//self.destroy();
this.do_action(action); this.do_action(action);
}, this)); }, this));
}, this)); }, this));

View File

@ -15,7 +15,7 @@
<button class="neworder-button">+</button> <button class="neworder-button">+</button>
<ol id="orders"></ol> <ol id="orders"></ol>
</div> </div>
<span class="placeholder-SynchNotificationWidget"></span> <!-- here goes header buttons -->
</div> </div>
</div> </div>
<div id="content"> <div id="content">
@ -44,6 +44,12 @@
</div> </div>
</t> </t>
<t t-name="HeaderButtonWidget">
<div class="header-button">
<t t-esc="widget.label" />
</div>
</t>
<t t-name="PosCloseWarning"> <t t-name="PosCloseWarning">
<div>There are pending operations that could not be saved into the database, are you sure you want to exit?</div> <div>There are pending operations that could not be saved into the database, are you sure you want to exit?</div>
</t> </t>
@ -83,7 +89,7 @@
<t t-name="CategoryButton"> <t t-name="CategoryButton">
<li class='category-button'> <li class='category-button'>
<div class="category-img"> <div class="category-img">
<img t-att-src="'data:image/gif;base64,'+ category.image_medium" /> <img src="" />
</div> </div>
<div class="category-name"> <div class="category-name">
<t t-esc="category.name"/> <t t-esc="category.name"/>
@ -91,6 +97,12 @@
</li> </li>
</t> </t>
<t t-name="CategorySimpleButton">
<li class='category-simple-button'>
<t t-esc="category.name"/>
</li>
</t>
<t t-name="ProductCategoriesWidget"> <t t-name="ProductCategoriesWidget">
<header> <header>
<ol class="breadcrumb"> <ol class="breadcrumb">
@ -220,7 +232,7 @@
<t t-if="widget.currency.position == 'after'" t-esc="widget.currency.symbol"/> <t t-if="widget.currency.position == 'after'" t-esc="widget.currency.symbol"/>
</span> </span>
</div> </div>
<div class="infoline"> <div class="infoline" >
<span class='left-block'> <span class='left-block'>
Change: Change:
</span> </span>
@ -252,6 +264,9 @@
<img src="/point_of_sale/static/src/img/scan.png" /> <img src="/point_of_sale/static/src/img/scan.png" />
<p> Please scan an item or your member card </p> <p> Please scan an item or your member card </p>
</div> </div>
<div class="goodbye-message">
<p>Thank you for shopping with us.</p>
</div>
</t> </t>
@ -342,7 +357,7 @@
<li class='product'> <li class='product'>
<a href="#"> <a href="#">
<div class="product-img"> <div class="product-img">
<img t-att-src="'data:image/gif;base64,'+ widget.model.get('image_medium')" /> <img src='' /> <!-- the product thumbnail -->
<t t-if="!widget.model.get('to_weight')"> <t t-if="!widget.model.get('to_weight')">
<span class="price-tag"> <span class="price-tag">
<t t-esc="widget.format_currency(widget.model.get('list_price'))"/> <t t-esc="widget.format_currency(widget.model.get('list_price'))"/>
@ -493,7 +508,7 @@
<tr><td>Tax:</td><td class="pos-right-align"> <tr><td>Tax:</td><td class="pos-right-align">
<t t-esc="widget.format_currency(widget.currentOrder.getTax().toFixed(2))"/> <t t-esc="widget.format_currency(widget.currentOrder.getTax().toFixed(2))"/>
</td></tr> </td></tr>
<tr><td>Total:</td><td class="pos-right-align"> <tr class="emph"><td>Total:</td><td class="pos-right-align">
<t t-esc="widget.format_currency(widget.currentOrder.getTotal().toFixed(2))"/> <t t-esc="widget.format_currency(widget.currentOrder.getTotal().toFixed(2))"/>
</td></tr> </td></tr>
</table> </table>
@ -519,30 +534,7 @@
<t t-name="ActionBarWidget"> <t t-name="ActionBarWidget">
<div class="pos-actionbar"> <div class="pos-actionbar">
<div class="pos-actionbar-left-pane">
<div class="button help-button">
<div class='icon'>
<img src="/point_of_sale/static/src/img/icons/png48/help.png" />
<div class='iconlabel'>Help</div>
</div>
</div>
<div class="button close-button">
<div class='icon'>
<img src="/point_of_sale/static/src/img/icons/png48/shut-down.png" />
<div class='iconlabel'>Close</div>
</div>
</div>
<div class="button logout-button">
<div class='icon'>
<img src="/point_of_sale/static/src/img/icons/png48/system-log-out.png" />
<div class='iconlabel'>Client Mode</div>
</div>
</div>
</div>
<ul class="pos-actionbar-button-list"> <ul class="pos-actionbar-button-list">
<!-- <li class="button">BUTTOOON</li>
<li class="button">JEEENKIINS</li>
<li class="button rightalign">ARGH</li> -->
</ul> </ul>
</div> </div>
</t> </t>

View File

@ -1,22 +0,0 @@
<!doctype html>
<html>
<head>
<title>JSON</title>
<script type="text/javascript" src="http://localhost:8069/web/webclient/js"></script>
</head>
<body>
<h1>JSON</h1>
<p id="response"></p>
<script type="text/javascript">
var c = new openerp.init(['web', 'point_of_sale']);
c.connection.session_bind('http://localhost:8069').then(function() {
var w = new c.point_of_sale.test();
w.appendTo($("#response"));
});
</script>
</body>
</html>

View File

@ -9,9 +9,3 @@
- -
!python {model: pos.open.statement}: | !python {model: pos.open.statement}: |
self.open_statement(cr, uid, [ref('new_statement_open')], context={}) self.open_statement(cr, uid, [ref('new_statement_open')], context={})
-
I check that I have some bank statements open for the admin user
-
!python {model: account.bank.statement}: |
ids = self.search(cr, uid, [('state', 'in', ('open','new')), ('user_id', '=', 1)])
assert (len(ids)>0), 'No statement open for the admin user!'

View File

@ -12,7 +12,7 @@
account_collected_id: account.iva account_collected_id: account.iva
price_include: 1 price_include: 1
- -
I assign this 10 percent tax on the PC1 product as a sale tax I assign this 10 percent tax on the [PCSC234] PC Assemble SC234 product as a sale tax
- -
!record {model: product.product, id: product.product_product_3}: !record {model: product.product, id: product.product_product_3}:
taxes_id: [account_tax_10_incl] taxes_id: [account_tax_10_incl]
@ -27,7 +27,7 @@
account_collected_id: account.iva account_collected_id: account.iva
price_include: 0 price_include: 0
- -
I assign this 5 percent tax on the PC2 product as a sale tax I assign this 5 percent tax on the PCSC349 product as a sale tax
- -
!record {model: product.product, id: product.product_product_4}: !record {model: product.product, id: product.product_product_4}:
taxes_id: [account_tax_05_incl] taxes_id: [account_tax_05_incl]
@ -52,7 +52,7 @@
- -
!workflow {model: pos.session, action: open, ref: pos_order_session0} !workflow {model: pos.session, action: open, ref: pos_order_session0}
- -
I create a PoS order with 2 units of PC1 at 450 EUR (Tax Incl) and 3 units of PC2 at 300 EUR. (Tax Excl) I create a PoS order with 2 units of PCSC234 at 450 EUR (Tax Incl) and 3 units of PCSC349 at 300 EUR. (Tax Excl)
- -
!record {model: pos.order, id: pos_order_pos0}: !record {model: pos.order, id: pos_order_pos0}:
company_id: base.main_company company_id: base.main_company

View File

@ -1,5 +1,5 @@
- -
I create a new PoS order with 2 units of PC1 at 450 EUR (Tax Incl) and 3 units of PC2 at 300 EUR. (Tax Excl) I create a new PoS order with 2 units of PC1 at 450 EUR (Tax Incl) and 3 units of PCSC349 at 300 EUR. (Tax Excl)
- -
!record {model: pos.order, id: pos_order_pos1}: !record {model: pos.order, id: pos_order_pos1}:
company_id: base.main_company company_id: base.main_company

View File

@ -0,0 +1,56 @@
#!/usr/bin/python
import sys
import re
import math
def ean_checksum(eancode):
"""returns the checksum of an ean string of length 13, returns -1 if the string has the wrong length"""
if len(eancode) <> 13:
return -1
oddsum=0
evensum=0
total=0
eanvalue=eancode
reversevalue = eanvalue[::-1]
finalean=reversevalue[1:]
for i in range(len(finalean)):
if i % 2 == 0:
oddsum += int(finalean[i])
else:
evensum += int(finalean[i])
total=(oddsum * 3) + evensum
check = int(10 - math.ceil(total % 10.0)) %10
return check
def check_ean(eancode):
"""returns True if eancode is a valid ean13 string, or null"""
if not eancode:
return True
if len(eancode) <> 13:
return False
try:
int(eancode)
except:
return False
return ean_checksum(eancode) == int(eancode[-1])
def sanitize_ean13(ean13):
"""Creates and returns a valid ean13 from an invalid one"""
if not ean13:
return "0000000000000"
ean13 = re.sub("[A-Za-z]","0",ean13);
ean13 = re.sub("[^0-9]","",ean13);
ean13 = ean13[:13]
if len(ean13) < 13:
ean13 = ean13 + '0' * (13-len(ean13))
return ean13[:-1] + str(ean_checksum(ean13))
def main():
for arg in sys.argv[1:]:
print sanitize_ean13(arg)
if __name__ == '__main__':
main()

View File

@ -4,13 +4,17 @@ from osv import osv, fields
from tools.translate import _ from tools.translate import _
import netsvc import netsvc
from openerp.addons.point_of_sale.point_of_sale import pos_session
class pos_session_opening(osv.osv_memory): class pos_session_opening(osv.osv_memory):
_name = 'pos.session.opening' _name = 'pos.session.opening'
_columns = { _columns = {
'pos_config_id' : fields.many2one('pos.config', 'Point of Sale', required=True), 'pos_config_id' : fields.many2one('pos.config', 'Point of Sale', required=True),
'pos_session_id' : fields.many2one('pos.session', 'PoS Session'), 'pos_session_id' : fields.many2one('pos.session', 'PoS Session'),
'pos_state' : fields.char('Session State'), 'pos_state' : fields.selection(pos_session.POS_SESSION_STATE,
'Session State', readonly=True),
'show_config' : fields.boolean('Show Config', readonly=True),
} }
def open_ui(self, cr, uid, ids, context=None): def open_ui(self, cr, uid, ids, context=None):
@ -19,7 +23,7 @@ class pos_session_opening(osv.osv_memory):
context['active_id'] = data.pos_session_id.id context['active_id'] = data.pos_session_id.id
return { return {
'type' : 'ir.actions.client', 'type' : 'ir.actions.client',
'name' : 'Start Point Of Sale', 'name' : _('Start Point Of Sale'),
'tag' : 'pos.ui', 'tag' : 'pos.ui',
'context' : context 'context' : context
} }
@ -87,7 +91,11 @@ class pos_session_opening(osv.osv_memory):
if not result: if not result:
r = self.pool.get('pos.config').search(cr, uid, [], context=context) r = self.pool.get('pos.config').search(cr, uid, [], context=context)
result = r and r[0] or False result = r and r[0] or False
count = self.pool.get('pos.config').search_count(cr, uid, [('state', '=', 'active')], context=context)
show_config = bool(count > 1)
return { return {
'pos_config_id' : result 'pos_config_id' : result,
'show_config' : show_config,
} }
pos_session_opening() pos_session_opening()

View File

@ -6,14 +6,15 @@
<field name="model">pos.session.opening</field> <field name="model">pos.session.opening</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<form string="PoS Session Opening" version="7.0"> <form string="PoS Session Opening" version="7.0">
<separator string="Select your Point of Sale" colspan="4" /> <field name="show_config" invisible="1" />
<group> <separator string="Select your Point of Sale" colspan="4" attrs="{'invisible' : [('show_config', '=', False)]}" />
<group attrs="{'invisible' : [('show_config', '=', False)]}">
<field name="pos_config_id" on_change="on_change_config(pos_config_id)" <field name="pos_config_id" on_change="on_change_config(pos_config_id)"
widget="selection" domain="[('state','=','active')]" widget="selection" domain="[('state','=','active')]"
class="oe_inline"/> class="oe_inline"/>
<field name="pos_state" class="oe_inline" attrs="{'invisible' : [('pos_state', '=', False)]}" />
</group> </group>
<field name="pos_session_id" invisible="1"/> <field name="pos_session_id" invisible="1"/>
<field name="pos_state" invisible="1"/>
<button name="open_ui" type="object" string="Start Selling" <button name="open_ui" type="object" string="Start Selling"
attrs="{'invisible' : [('pos_state', 'not in', ('opened',))]}" attrs="{'invisible' : [('pos_state', 'not in', ('opened',))]}"
class="oe_highlight" class="oe_highlight"

View File

@ -25,7 +25,8 @@
'depends': [ 'depends': [
'base', 'base',
'share', 'share',
'auth_anonymous' 'auth_anonymous',
'auth_signup',
], ],
'author': 'OpenERP SA', 'author': 'OpenERP SA',
'category': 'Portal', 'category': 'Portal',
@ -45,8 +46,8 @@ very handy when used in combination with the module 'share'.
'data': [ 'data': [
'security/portal_security.xml', 'security/portal_security.xml',
'security/ir.model.access.csv', 'security/ir.model.access.csv',
'portal_view.xml',
'portal_data.xml', 'portal_data.xml',
'portal_view.xml',
'wizard/portal_wizard_view.xml', 'wizard/portal_wizard_view.xml',
'wizard/share_wizard_view.xml', 'wizard/share_wizard_view.xml',
], ],

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<openerp> <openerp>
<data> <data noupdate="1">
<record id="portal" model="res.portal"> <record id="portal" model="res.portal">
<field name="name">Portal</field> <field name="name">Portal</field>
@ -10,7 +10,7 @@
<!-- Mail group for the company's news --> <!-- Mail group for the company's news -->
<record id="company_news_feed" model="mail.group"> <record id="company_news_feed" model="mail.group">
<field name="name">Company's news feed</field> <field name="name">Company's news</field>
</record> </record>
<record id="action_news" model="ir.actions.act_window"> <record id="action_news" model="ir.actions.act_window">
@ -32,24 +32,5 @@
<field name="view_mode">form</field> <field name="view_mode">form</field>
</record> </record>
<!-- Top menu item -->
<menuitem name="Portal"
id="portal_menu"
groups="base.group_no_one,portal.group_portal_member,auth_anonymous.group_anonymous"
sequence="20"/>
<menuitem name="Our company" id="portal_company" parent="portal_menu" sequence="10"/>
<menuitem name="News" id="portal_company_news" parent="portal_company" sequence="10" action="action_news"/>
<menuitem name="Jobs" id="portal_jobs" parent="portal_company" sequence="20" action="action_jobs"/>
<!--
Create menu items that we'll leave empty for now - they'll be
filled up by other portal modules.
-->
<menuitem name="Orders" id="portal_orders" parent="portal_menu" sequence="20"/>
<menuitem name="Invoices and Payments" id="portal_invoices_payements" parent="portal_menu" sequence="30"/>
<menuitem name="Projects" id="portal_projects" parent="portal_menu" sequence="40"/>
<menuitem name="After Sale Services" id="portal_after_sales" parent="portal_menu" sequence="50"/>
</data> </data>
</openerp> </openerp>

View File

@ -2,6 +2,25 @@
<openerp> <openerp>
<data> <data>
<!-- Top menu item -->
<menuitem name="Portal"
id="portal_menu"
groups="base.group_no_one,portal.group_portal_member,auth_anonymous.group_anonymous"
sequence="20"/>
<menuitem name="Our company" id="portal_company" parent="portal_menu" sequence="10"/>
<menuitem name="News" id="portal_company_news" parent="portal_company" sequence="10" action="action_news"/>
<menuitem name="Jobs" id="portal_jobs" parent="portal_company" sequence="20" action="action_jobs"/>
<!--
Create menu items that we'll leave empty for now - they'll be
filled up by other portal modules.
-->
<menuitem name="Orders" id="portal_orders" parent="portal_menu" sequence="20"/>
<menuitem name="Invoices and Payments" id="portal_invoices_payements" parent="portal_menu" sequence="30"/>
<menuitem name="Projects" id="portal_projects" parent="portal_menu" sequence="40"/>
<menuitem name="After Sale Services" id="portal_after_sales" parent="portal_menu" sequence="50"/>
<!-- portal tree view --> <!-- portal tree view -->
<record id="portal_list_view" model="ir.ui.view"> <record id="portal_list_view" model="ir.ui.view">
<field name="name">Portal List</field> <field name="name">Portal List</field>

View File

@ -28,18 +28,10 @@ import re
import tools import tools
from tools.translate import _ from tools.translate import _
def is_pair(x): def ean_checksum(eancode):
return not x%2 """returns the checksum of an ean string of length 13, returns -1 if the string has the wrong length"""
def check_ean(eancode):
if not eancode:
return True
if len(eancode) <> 13: if len(eancode) <> 13:
return False return -1
try:
int(eancode)
except:
return False
oddsum=0 oddsum=0
evensum=0 evensum=0
total=0 total=0
@ -48,17 +40,38 @@ def check_ean(eancode):
finalean=reversevalue[1:] finalean=reversevalue[1:]
for i in range(len(finalean)): for i in range(len(finalean)):
if is_pair(i): if i % 2 == 0:
oddsum += int(finalean[i]) oddsum += int(finalean[i])
else: else:
evensum += int(finalean[i]) evensum += int(finalean[i])
total=(oddsum * 3) + evensum total=(oddsum * 3) + evensum
check = int(10 - math.ceil(total % 10.0)) %10 check = int(10 - math.ceil(total % 10.0)) %10
return check
if check != int(eancode[-1]): def check_ean(eancode):
"""returns True if eancode is a valid ean13 string, or null"""
if not eancode:
return True
if len(eancode) <> 13:
return False return False
return True try:
int(eancode)
except:
return False
return ean_checksum(eancode) == int(eancode[-1])
def sanitize_ean13(ean13):
"""Creates and returns a valid ean13 from an invalid one"""
if not ean13:
return "0000000000000"
ean13 = re.sub("[A-Za-z]","0",ean13);
ean13 = re.sub("[^0-9]","",ean13);
ean13 = ean13[:13]
if len(ean13) < 13:
ean13 = ean13 + '0' * (13-len(ean13))
return ean13[:-1] + str(ean_checksum(ean13))
#---------------------------------------------------------- #----------------------------------------------------------
# UOM # UOM
#---------------------------------------------------------- #----------------------------------------------------------
@ -195,8 +208,10 @@ product_ul()
class product_category(osv.osv): class product_category(osv.osv):
def name_get(self, cr, uid, ids, context=None): def name_get(self, cr, uid, ids, context=None):
if not len(ids): if isinstance(ids, (list, tuple)) and not len(ids):
return [] return []
if isinstance(ids, (long, int)):
ids = [ids]
reads = self.read(cr, uid, ids, ['name','parent_id'], context=context) reads = self.read(cr, uid, ids, ['name','parent_id'], context=context)
res = [] res = []
for record in reads: for record in reads:
@ -594,6 +609,7 @@ class product_product(osv.osv):
res = check_ean(product['ean13']) res = check_ean(product['ean13'])
return res return res
_constraints = [(_check_ean_key, 'Error: Invalid ean code', ['ean13'])] _constraints = [(_check_ean_key, 'Error: Invalid ean code', ['ean13'])]
def on_order(self, cr, uid, ids, orderline, quantity): def on_order(self, cr, uid, ids, orderline, quantity):

View File

@ -6,7 +6,7 @@
to_date: !eval "'%s-12-31' %(datetime.now().year)" to_date: !eval "'%s-12-31' %(datetime.now().year)"
invoice_state: open_paid invoice_state: open_paid
- -
I open margin for PC3 I open margin for PC-DEM
- -
!python {model: product.margin}: | !python {model: product.margin}: |
self.action_open_window(cr, uid, [ref("product_margin_wiz0")], {"lang": 'en_US', self.action_open_window(cr, uid, [ref("product_margin_wiz0")], {"lang": 'en_US',

Some files were not shown because too many files have changed in this diff Show More