[MERGE]Merge trunk-website-al.
bzr revid: bth@tinyerp.com-20131015135019-hswfj4rqeqzzvkaz
This commit is contained in:
@@ -6,11 +6,11 @@
|
||||
<field name="model">product.product</field>
|
||||
<field name="inherit_id" ref="account.product_normal_form_view"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='property_account_expense']" position="after">
|
||||
<field name="property_account_expense" position="after">
|
||||
<label string="" colspan="2"/>
|
||||
<field name="property_account_creditor_price_difference" domain="[('type','<>','view'),('type','<>','consolidation')]" attrs="{'readonly':[('purchase_ok','=',0)]}" />
|
||||
<newline/>
|
||||
</xpath>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
@@ -19,10 +19,10 @@
|
||||
<field name="model">product.template</field>
|
||||
<field name="inherit_id" ref="account.product_template_form_view"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="/form/notebook/page/field[@name='property_account_expense']" position="after">
|
||||
<field name="property_account_expense" position="after">
|
||||
<field name="property_account_creditor_price_difference" domain="[('type','<>','view'),('type','<>','consolidation')]" attrs="{'readonly':[('purchase_ok','=',0)]}" />
|
||||
<newline/>
|
||||
</xpath>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
|
||||
@@ -0,0 +1,357 @@
|
||||
# Danish translation for openobject-addons
|
||||
# Copyright (c) 2013 Rosetta Contributors and Canonical Ltd 2013
|
||||
# This file is distributed under the same license as the openobject-addons package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, 2013.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: openobject-addons\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2012-12-21 17:05+0000\n"
|
||||
"PO-Revision-Date: 2013-10-15 00:23+0000\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: Danish <da@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2013-10-15 04:41+0000\n"
|
||||
"X-Generator: Launchpad (build 16799)\n"
|
||||
|
||||
#. module: account_bank_statement_extensions
|
||||
#: help:account.bank.statement.line.global,name:0
|
||||
msgid "Originator to Beneficiary Information"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_bank_statement_extensions
|
||||
#: view:account.bank.statement.line:0
|
||||
#: selection:account.bank.statement.line,state:0
|
||||
msgid "Confirmed"
|
||||
msgstr "Bekræftet"
|
||||
|
||||
#. module: account_bank_statement_extensions
|
||||
#: view:account.bank.statement:0
|
||||
#: view:account.bank.statement.line:0
|
||||
msgid "Glob. Id"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_bank_statement_extensions
|
||||
#: selection:account.bank.statement.line.global,type:0
|
||||
msgid "CODA"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_bank_statement_extensions
|
||||
#: field:account.bank.statement.line.global,parent_id:0
|
||||
msgid "Parent Code"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_bank_statement_extensions
|
||||
#: view:account.bank.statement.line:0
|
||||
msgid "Debit"
|
||||
msgstr "Debet"
|
||||
|
||||
#. module: account_bank_statement_extensions
|
||||
#: view:cancel.statement.line:0
|
||||
#: model:ir.actions.act_window,name:account_bank_statement_extensions.action_cancel_statement_line
|
||||
#: model:ir.model,name:account_bank_statement_extensions.model_cancel_statement_line
|
||||
msgid "Cancel selected statement lines"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_bank_statement_extensions
|
||||
#: field:account.bank.statement.line,val_date:0
|
||||
msgid "Value Date"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_bank_statement_extensions
|
||||
#: view:account.bank.statement.line:0
|
||||
msgid "Group By..."
|
||||
msgstr "Gruppér efter..."
|
||||
|
||||
#. module: account_bank_statement_extensions
|
||||
#: view:account.bank.statement.line:0
|
||||
#: selection:account.bank.statement.line,state:0
|
||||
msgid "Draft"
|
||||
msgstr "Kladde"
|
||||
|
||||
#. module: account_bank_statement_extensions
|
||||
#: view:account.bank.statement.line:0
|
||||
msgid "Statement"
|
||||
msgstr "Erklæring"
|
||||
|
||||
#. module: account_bank_statement_extensions
|
||||
#: view:confirm.statement.line:0
|
||||
#: model:ir.actions.act_window,name:account_bank_statement_extensions.action_confirm_statement_line
|
||||
#: model:ir.model,name:account_bank_statement_extensions.model_confirm_statement_line
|
||||
msgid "Confirm selected statement lines"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_bank_statement_extensions
|
||||
#: report:bank.statement.balance.report:0
|
||||
#: model:ir.actions.report.xml,name:account_bank_statement_extensions.bank_statement_balance_report
|
||||
msgid "Bank Statement Balances Report"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_bank_statement_extensions
|
||||
#: view:cancel.statement.line:0
|
||||
msgid "Cancel Lines"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_bank_statement_extensions
|
||||
#: view:account.bank.statement.line.global:0
|
||||
#: model:ir.model,name:account_bank_statement_extensions.model_account_bank_statement_line_global
|
||||
msgid "Batch Payment Info"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_bank_statement_extensions
|
||||
#: field:account.bank.statement.line,state:0
|
||||
msgid "Status"
|
||||
msgstr "Status"
|
||||
|
||||
#. module: account_bank_statement_extensions
|
||||
#: code:addons/account_bank_statement_extensions/account_bank_statement.py:129
|
||||
#, python-format
|
||||
msgid ""
|
||||
"Delete operation not allowed. Please go to the associated bank "
|
||||
"statement in order to delete and/or modify bank statement line."
|
||||
msgstr ""
|
||||
|
||||
#. module: account_bank_statement_extensions
|
||||
#: view:confirm.statement.line:0
|
||||
msgid "or"
|
||||
msgstr "eller"
|
||||
|
||||
#. module: account_bank_statement_extensions
|
||||
#: view:confirm.statement.line:0
|
||||
msgid "Confirm Lines"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_bank_statement_extensions
|
||||
#: view:account.bank.statement.line.global:0
|
||||
msgid "Transactions"
|
||||
msgstr "Transaktioner"
|
||||
|
||||
#. module: account_bank_statement_extensions
|
||||
#: field:account.bank.statement.line.global,type:0
|
||||
msgid "Type"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_bank_statement_extensions
|
||||
#: view:account.bank.statement.line:0
|
||||
#: report:bank.statement.balance.report:0
|
||||
msgid "Journal"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_bank_statement_extensions
|
||||
#: view:account.bank.statement.line:0
|
||||
msgid "Confirmed Statement Lines."
|
||||
msgstr ""
|
||||
|
||||
#. module: account_bank_statement_extensions
|
||||
#: view:account.bank.statement.line:0
|
||||
msgid "Credit Transactions."
|
||||
msgstr ""
|
||||
|
||||
#. module: account_bank_statement_extensions
|
||||
#: model:ir.actions.act_window,help:account_bank_statement_extensions.action_cancel_statement_line
|
||||
msgid "cancel selected statement lines."
|
||||
msgstr ""
|
||||
|
||||
#. module: account_bank_statement_extensions
|
||||
#: field:account.bank.statement.line,counterparty_number:0
|
||||
msgid "Counterparty Number"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_bank_statement_extensions
|
||||
#: report:bank.statement.balance.report:0
|
||||
msgid "Closing Balance"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_bank_statement_extensions
|
||||
#: report:bank.statement.balance.report:0
|
||||
msgid "Date"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_bank_statement_extensions
|
||||
#: view:account.bank.statement.line:0
|
||||
#: field:account.bank.statement.line,globalisation_amount:0
|
||||
msgid "Glob. Amount"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_bank_statement_extensions
|
||||
#: view:account.bank.statement.line:0
|
||||
msgid "Debit Transactions."
|
||||
msgstr ""
|
||||
|
||||
#. module: account_bank_statement_extensions
|
||||
#: view:account.bank.statement.line:0
|
||||
msgid "Extended Filters..."
|
||||
msgstr ""
|
||||
|
||||
#. module: account_bank_statement_extensions
|
||||
#: view:confirm.statement.line:0
|
||||
msgid "Confirmed lines cannot be changed anymore."
|
||||
msgstr ""
|
||||
|
||||
#. module: account_bank_statement_extensions
|
||||
#: view:cancel.statement.line:0
|
||||
msgid "Are you sure you want to cancel the selected Bank Statement lines ?"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_bank_statement_extensions
|
||||
#: report:bank.statement.balance.report:0
|
||||
msgid "Name"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_bank_statement_extensions
|
||||
#: field:account.bank.statement.line.global,name:0
|
||||
msgid "OBI"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_bank_statement_extensions
|
||||
#: selection:account.bank.statement.line.global,type:0
|
||||
msgid "ISO 20022"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_bank_statement_extensions
|
||||
#: view:account.bank.statement.line:0
|
||||
msgid "Notes"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_bank_statement_extensions
|
||||
#: selection:account.bank.statement.line.global,type:0
|
||||
msgid "Manual"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_bank_statement_extensions
|
||||
#: view:account.bank.statement.line:0
|
||||
msgid "Bank Transaction"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_bank_statement_extensions
|
||||
#: view:account.bank.statement.line:0
|
||||
msgid "Credit"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_bank_statement_extensions
|
||||
#: field:account.bank.statement.line.global,amount:0
|
||||
msgid "Amount"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_bank_statement_extensions
|
||||
#: view:account.bank.statement.line:0
|
||||
msgid "Fin.Account"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_bank_statement_extensions
|
||||
#: field:account.bank.statement.line,counterparty_currency:0
|
||||
msgid "Counterparty Currency"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_bank_statement_extensions
|
||||
#: field:account.bank.statement.line,counterparty_bic:0
|
||||
msgid "Counterparty BIC"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_bank_statement_extensions
|
||||
#: field:account.bank.statement.line.global,child_ids:0
|
||||
msgid "Child Codes"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_bank_statement_extensions
|
||||
#: view:account.bank.statement.line:0
|
||||
msgid "Search Bank Transactions"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_bank_statement_extensions
|
||||
#: view:confirm.statement.line:0
|
||||
msgid "Are you sure you want to confirm the selected Bank Statement lines ?"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_bank_statement_extensions
|
||||
#: help:account.bank.statement.line,globalisation_id:0
|
||||
msgid ""
|
||||
"Code to identify transactions belonging to the same globalisation level "
|
||||
"within a batch payment"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_bank_statement_extensions
|
||||
#: view:account.bank.statement.line:0
|
||||
msgid "Draft Statement Lines."
|
||||
msgstr ""
|
||||
|
||||
#. module: account_bank_statement_extensions
|
||||
#: view:account.bank.statement.line:0
|
||||
msgid "Glob. Am."
|
||||
msgstr ""
|
||||
|
||||
#. module: account_bank_statement_extensions
|
||||
#: model:ir.model,name:account_bank_statement_extensions.model_account_bank_statement_line
|
||||
msgid "Bank Statement Line"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_bank_statement_extensions
|
||||
#: field:account.bank.statement.line.global,code:0
|
||||
msgid "Code"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_bank_statement_extensions
|
||||
#: field:account.bank.statement.line,counterparty_name:0
|
||||
msgid "Counterparty Name"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_bank_statement_extensions
|
||||
#: model:ir.model,name:account_bank_statement_extensions.model_res_partner_bank
|
||||
msgid "Bank Accounts"
|
||||
msgstr "Bankkonti"
|
||||
|
||||
#. module: account_bank_statement_extensions
|
||||
#: model:ir.model,name:account_bank_statement_extensions.model_account_bank_statement
|
||||
msgid "Bank Statement"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_bank_statement_extensions
|
||||
#: view:account.bank.statement.line:0
|
||||
msgid "Statement Line"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_bank_statement_extensions
|
||||
#: sql_constraint:account.bank.statement.line.global:0
|
||||
msgid "The code must be unique !"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_bank_statement_extensions
|
||||
#: field:account.bank.statement.line.global,bank_statement_line_ids:0
|
||||
#: model:ir.actions.act_window,name:account_bank_statement_extensions.action_bank_statement_line
|
||||
#: model:ir.ui.menu,name:account_bank_statement_extensions.bank_statement_line
|
||||
msgid "Bank Statement Lines"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_bank_statement_extensions
|
||||
#: code:addons/account_bank_statement_extensions/account_bank_statement.py:129
|
||||
#, python-format
|
||||
msgid "Warning!"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_bank_statement_extensions
|
||||
#: view:account.bank.statement.line.global:0
|
||||
msgid "Child Batch Payments"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_bank_statement_extensions
|
||||
#: view:confirm.statement.line:0
|
||||
msgid "Cancel"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_bank_statement_extensions
|
||||
#: view:account.bank.statement.line:0
|
||||
msgid "Statement Lines"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_bank_statement_extensions
|
||||
#: view:account.bank.statement.line:0
|
||||
msgid "Total Amount"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_bank_statement_extensions
|
||||
#: field:account.bank.statement.line,globalisation_id:0
|
||||
msgid "Globalisation ID"
|
||||
msgstr ""
|
||||
@@ -0,0 +1,256 @@
|
||||
# Danish translation for openobject-addons
|
||||
# Copyright (c) 2013 Rosetta Contributors and Canonical Ltd 2013
|
||||
# This file is distributed under the same license as the openobject-addons package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, 2013.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: openobject-addons\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2012-12-21 17:05+0000\n"
|
||||
"PO-Revision-Date: 2013-10-14 19:01+0000\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: Danish <da@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2013-10-15 04:41+0000\n"
|
||||
"X-Generator: Launchpad (build 16799)\n"
|
||||
|
||||
#. module: base_gengo
|
||||
#: view:res.company:0
|
||||
msgid "Comments for Translator"
|
||||
msgstr "Bemærkning til oversætter"
|
||||
|
||||
#. module: base_gengo
|
||||
#: field:ir.translation,job_id:0
|
||||
msgid "Gengo Job ID"
|
||||
msgstr "Gengo Job ID"
|
||||
|
||||
#. module: base_gengo
|
||||
#: code:addons/base_gengo/wizard/base_gengo_translations.py:114
|
||||
#, python-format
|
||||
msgid "This language is not supported by the Gengo translation services."
|
||||
msgstr "Dette sprog er ikke supporteret af Gengo oversættelses service."
|
||||
|
||||
#. module: base_gengo
|
||||
#: field:res.company,gengo_comment:0
|
||||
msgid "Comments"
|
||||
msgstr "Bemærkninger"
|
||||
|
||||
#. module: base_gengo
|
||||
#: field:res.company,gengo_private_key:0
|
||||
msgid "Gengo Private Key"
|
||||
msgstr "Gengo Privat nøgle"
|
||||
|
||||
#. module: base_gengo
|
||||
#: model:ir.model,name:base_gengo.model_base_gengo_translations
|
||||
msgid "base.gengo.translations"
|
||||
msgstr "base.gengo.translations"
|
||||
|
||||
#. module: base_gengo
|
||||
#: help:res.company,gengo_auto_approve:0
|
||||
msgid "Jobs are Automatically Approved by Gengo."
|
||||
msgstr "Opgaver er automatisk godkendt af Gengo."
|
||||
|
||||
#. module: base_gengo
|
||||
#: field:base.gengo.translations,lang_id:0
|
||||
msgid "Language"
|
||||
msgstr "Sprog"
|
||||
|
||||
#. module: base_gengo
|
||||
#: field:ir.translation,gengo_comment:0
|
||||
msgid "Comments & Activity Linked to Gengo"
|
||||
msgstr "Bemærkninger og aktiviteter forbundet til Gengo"
|
||||
|
||||
#. module: base_gengo
|
||||
#: code:addons/base_gengo/wizard/base_gengo_translations.py:124
|
||||
#, python-format
|
||||
msgid "Gengo Sync Translation (Response)"
|
||||
msgstr "Gengo synk oversættelse (Svar)"
|
||||
|
||||
#. module: base_gengo
|
||||
#: code:addons/base_gengo/wizard/base_gengo_translations.py:72
|
||||
#, python-format
|
||||
msgid ""
|
||||
"Gengo `Public Key` or `Private Key` are missing. Enter your Gengo "
|
||||
"authentication parameters under `Settings > Companies > Gengo Parameters`."
|
||||
msgstr ""
|
||||
"Gengo 'Offentlig nøgle' eller 'Private nøgle' mangler. Indtast din Gengo "
|
||||
"godkendelses oplysninger under 'Opsætning > Firmaer > Gengo opsætning'."
|
||||
|
||||
#. module: base_gengo
|
||||
#: selection:ir.translation,gengo_translation:0
|
||||
msgid "Translation By Machine"
|
||||
msgstr "Maskinel oversættelse"
|
||||
|
||||
#. module: base_gengo
|
||||
#: code:addons/base_gengo/wizard/base_gengo_translations.py:155
|
||||
#, python-format
|
||||
msgid ""
|
||||
"%s\n"
|
||||
"\n"
|
||||
"--\n"
|
||||
" Commented on %s by %s."
|
||||
msgstr ""
|
||||
"%s\n"
|
||||
"\n"
|
||||
"--\n"
|
||||
" Kommenteret på %s af %s."
|
||||
|
||||
#. module: base_gengo
|
||||
#: field:ir.translation,gengo_translation:0
|
||||
msgid "Gengo Translation Service Level"
|
||||
msgstr "Gengo Oversættelses service niveau"
|
||||
|
||||
#. module: base_gengo
|
||||
#: constraint:ir.translation:0
|
||||
msgid ""
|
||||
"The Gengo translation service selected is not supported for this language."
|
||||
msgstr ""
|
||||
"Den valgte Genko oversættelses service er ikke supporteret for dette sprog."
|
||||
|
||||
#. module: base_gengo
|
||||
#: selection:ir.translation,gengo_translation:0
|
||||
msgid "Standard"
|
||||
msgstr "Standard"
|
||||
|
||||
#. module: base_gengo
|
||||
#: help:ir.translation,gengo_translation:0
|
||||
msgid ""
|
||||
"You can select here the service level you want for an automatic translation "
|
||||
"using Gengo."
|
||||
msgstr ""
|
||||
|
||||
#. module: base_gengo
|
||||
#: field:base.gengo.translations,restart_send_job:0
|
||||
msgid "Restart Sending Job"
|
||||
msgstr ""
|
||||
|
||||
#. module: base_gengo
|
||||
#: view:ir.translation:0
|
||||
msgid "To Approve In Gengo"
|
||||
msgstr ""
|
||||
|
||||
#. module: base_gengo
|
||||
#: view:res.company:0
|
||||
msgid "Private Key"
|
||||
msgstr "Privat nøgle"
|
||||
|
||||
#. module: base_gengo
|
||||
#: view:res.company:0
|
||||
msgid "Public Key"
|
||||
msgstr "Offentlig nøgle"
|
||||
|
||||
#. module: base_gengo
|
||||
#: field:res.company,gengo_public_key:0
|
||||
msgid "Gengo Public Key"
|
||||
msgstr "Gengo offentlig nøgle"
|
||||
|
||||
#. module: base_gengo
|
||||
#: code:addons/base_gengo/wizard/base_gengo_translations.py:123
|
||||
#, python-format
|
||||
msgid "Gengo Sync Translation (Request)"
|
||||
msgstr ""
|
||||
|
||||
#. module: base_gengo
|
||||
#: view:ir.translation:0
|
||||
msgid "Translations"
|
||||
msgstr "Oversættelser"
|
||||
|
||||
#. module: base_gengo
|
||||
#: field:res.company,gengo_auto_approve:0
|
||||
msgid "Auto Approve Translation ?"
|
||||
msgstr ""
|
||||
|
||||
#. module: base_gengo
|
||||
#: model:ir.actions.act_window,name:base_gengo.action_wizard_base_gengo_translations
|
||||
#: model:ir.ui.menu,name:base_gengo.menu_action_wizard_base_gengo_translations
|
||||
msgid "Gengo: Manual Request of Translation"
|
||||
msgstr ""
|
||||
|
||||
#. module: base_gengo
|
||||
#: code:addons/base_gengo/ir_translation.py:62
|
||||
#: code:addons/base_gengo/wizard/base_gengo_translations.py:109
|
||||
#, python-format
|
||||
msgid "Gengo Authentication Error"
|
||||
msgstr ""
|
||||
|
||||
#. module: base_gengo
|
||||
#: model:ir.model,name:base_gengo.model_res_company
|
||||
msgid "Companies"
|
||||
msgstr ""
|
||||
|
||||
#. module: base_gengo
|
||||
#: view:ir.translation:0
|
||||
msgid ""
|
||||
"Note: If the translation state is 'In Progress', it means that the "
|
||||
"translation has to be approved to be uploaded in this system. You are "
|
||||
"supposed to do that directly by using your Gengo Account"
|
||||
msgstr ""
|
||||
|
||||
#. module: base_gengo
|
||||
#: code:addons/base_gengo/wizard/base_gengo_translations.py:82
|
||||
#, python-format
|
||||
msgid ""
|
||||
"Gengo connection failed with this message:\n"
|
||||
"``%s``"
|
||||
msgstr ""
|
||||
|
||||
#. module: base_gengo
|
||||
#: view:res.company:0
|
||||
msgid "Gengo Parameters"
|
||||
msgstr ""
|
||||
|
||||
#. module: base_gengo
|
||||
#: view:base.gengo.translations:0
|
||||
msgid "Send"
|
||||
msgstr ""
|
||||
|
||||
#. module: base_gengo
|
||||
#: selection:ir.translation,gengo_translation:0
|
||||
msgid "Ultra"
|
||||
msgstr ""
|
||||
|
||||
#. module: base_gengo
|
||||
#: model:ir.model,name:base_gengo.model_ir_translation
|
||||
msgid "ir.translation"
|
||||
msgstr ""
|
||||
|
||||
#. module: base_gengo
|
||||
#: view:ir.translation:0
|
||||
msgid "Gengo Translation Service"
|
||||
msgstr ""
|
||||
|
||||
#. module: base_gengo
|
||||
#: selection:ir.translation,gengo_translation:0
|
||||
msgid "Pro"
|
||||
msgstr ""
|
||||
|
||||
#. module: base_gengo
|
||||
#: view:base.gengo.translations:0
|
||||
msgid "Gengo Request Form"
|
||||
msgstr ""
|
||||
|
||||
#. module: base_gengo
|
||||
#: code:addons/base_gengo/wizard/base_gengo_translations.py:114
|
||||
#, python-format
|
||||
msgid "Warning"
|
||||
msgstr ""
|
||||
|
||||
#. module: base_gengo
|
||||
#: help:res.company,gengo_comment:0
|
||||
msgid ""
|
||||
"This comment will be automatically be enclosed in each an every request sent "
|
||||
"to Gengo"
|
||||
msgstr ""
|
||||
|
||||
#. module: base_gengo
|
||||
#: view:base.gengo.translations:0
|
||||
msgid "Cancel"
|
||||
msgstr ""
|
||||
|
||||
#. module: base_gengo
|
||||
#: view:base.gengo.translations:0
|
||||
msgid "or"
|
||||
msgstr ""
|
||||
@@ -1,12 +1,10 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import simplejson
|
||||
|
||||
import openerp
|
||||
from openerp.addons.web.http import Controller, route
|
||||
|
||||
class ImportController(openerp.addons.web.http.Controller):
|
||||
_cp_path = '/base_import'
|
||||
|
||||
@openerp.addons.web.http.httprequest
|
||||
class ImportController(Controller):
|
||||
@route('/base_import/set_file')
|
||||
def set_file(self, req, file, import_id, jsonp='callback'):
|
||||
import_id = int(import_id)
|
||||
|
||||
|
||||
@@ -224,12 +224,13 @@ class ir_import(orm.TransientModel):
|
||||
headers, matches = self._match_headers(rows, fields, options)
|
||||
# Match should have consumed the first row (iif headers), get
|
||||
# the ``count`` next rows for preview
|
||||
preview = itertools.islice(rows, count)
|
||||
preview = list(itertools.islice(rows, count))
|
||||
assert preview, "CSV file seems to have no content"
|
||||
return {
|
||||
'fields': fields,
|
||||
'matches': matches or False,
|
||||
'headers': headers or False,
|
||||
'preview': list(preview),
|
||||
'preview': preview,
|
||||
}
|
||||
except Exception, e:
|
||||
# Due to lazy generators, UnicodeDecodeError (for
|
||||
|
||||
@@ -26,9 +26,6 @@ from openerp.report.render.rml2pdf import customfonts
|
||||
class base_config_settings(osv.osv_memory):
|
||||
_name = 'base.config.settings'
|
||||
_inherit = 'res.config.settings'
|
||||
|
||||
def _get_font(self, cr, uid, context=None):
|
||||
return sorted(customfonts.RegisterCustomFonts())
|
||||
|
||||
_columns = {
|
||||
'module_multi_company': fields.boolean('Manage multiple companies',
|
||||
@@ -44,11 +41,11 @@ class base_config_settings(osv.osv_memory):
|
||||
'module_base_import': fields.boolean("Allow users to import data from CSV files"),
|
||||
'module_google_drive': fields.boolean('Attach Google documents to any record',
|
||||
help="""This installs the module google_docs."""),
|
||||
'font': fields.selection(_get_font, "Select Font", help="Set the font into the report header, will be used for every RML report of the user company"),
|
||||
'font': fields.many2one('res.font', string="Report Font", help="Set the font into the report header, it will be used as default font in the RML reports of the user company"),
|
||||
}
|
||||
|
||||
_defaults= {
|
||||
'font': lambda self,cr,uid,c: self.pool.get('res.users').browse(cr, uid, uid, c).company_id.font or 'Helvetica',
|
||||
'font': lambda self,cr,uid,c: self.pool.get('res.users').browse(cr, uid, uid, c).company_id.font.id,
|
||||
}
|
||||
|
||||
def open_company(self, cr, uid, ids, context=None):
|
||||
@@ -74,9 +71,13 @@ class base_config_settings(osv.osv_memory):
|
||||
wizard = self.browse(cr, uid, ids)[0]
|
||||
if wizard.font:
|
||||
user = self.pool.get('res.users').browse(cr, uid, uid, context)
|
||||
user.company_id.write({'font':wizard.font,'rml_header': self._change_header(user.company_id.rml_header,wizard.font), 'rml_header2': self._change_header(user.company_id.rml_header2,wizard.font), 'rml_header3': self._change_header(user.company_id.rml_header3,wizard.font)})
|
||||
font_name = wizard.font.name
|
||||
user.company_id.write({'font': wizard.font.id,'rml_header': self._change_header(user.company_id.rml_header,font_name), 'rml_header2': self._change_header(user.company_id.rml_header2, font_name), 'rml_header3': self._change_header(user.company_id.rml_header3, font_name)})
|
||||
return {}
|
||||
|
||||
def act_discover_fonts(self, cr, uid, ids, context=None):
|
||||
return self.pool.get("res.font").discover_fonts(cr, uid, ids, context)
|
||||
|
||||
# Preferences wizard for Sales & CRM.
|
||||
# It is defined here because it is inherited independently in modules sale, crm,
|
||||
# plugin_outlook and plugin_thunderbird.
|
||||
|
||||
@@ -90,11 +90,12 @@
|
||||
</div>
|
||||
</div>
|
||||
</group>
|
||||
<group string="Report Settings">
|
||||
<label for="font" string="RML font (Header/footer)" />
|
||||
<group>
|
||||
<label for="font" />
|
||||
<div>
|
||||
<div>
|
||||
<field name="font" class="oe_inline"/>
|
||||
<button string="(reload fonts)" name="act_discover_fonts" type="object" class="oe_link"/>
|
||||
</div>
|
||||
</div>
|
||||
</group>
|
||||
|
||||
+330
-142
File diff suppressed because it is too large
Load Diff
@@ -618,12 +618,12 @@
|
||||
<xpath expr="//div[@name='buttons']" position="inside">
|
||||
<button string="Purchases" name="%(action_purchase_line_product_tree)d" type="action" groups="purchase.group_purchase_user"/>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='property_account_expense']" position="replace" >
|
||||
<field name="property_account_expense" position="replace" >
|
||||
<field name="property_account_expense" domain="[('type','<>','view'),('type','<>','consolidation')]" attrs="{'readonly':[('purchase_ok','=',0)]}" groups="account.group_account_user"/>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='supplier_taxes_id']" position="replace" >
|
||||
</field>
|
||||
<field name='supplier_taxes_id' position="replace" >
|
||||
<field name="supplier_taxes_id" colspan="2" widget="many2many_tags" attrs="{'readonly':[('purchase_ok','=',0)]}"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
</data>
|
||||
|
||||
@@ -17,8 +17,8 @@
|
||||
I verify that the onchange was correctly triggered
|
||||
-
|
||||
!assert {model: sale.order, id: sale.sale_order_test1, string: The onchange function of product was not correctly triggered}:
|
||||
- order_line[0].name == u'[LCD17] 17\u201d LCD Monitor'
|
||||
- order_line[0].price_unit == 1350.0
|
||||
- order_line[0].name == u'[A8767] Apple In-Ear Headphones'
|
||||
- order_line[0].price_unit == 79.0
|
||||
- order_line[0].product_uom_qty == 8
|
||||
- order_line[0].product_uom.id == ref('product.product_uom_unit')
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
I verify that the onchange was correctly triggered
|
||||
-
|
||||
!assert {model: sale.order, id: sale.sale_order_test2, string: The onchange function of product was not correctly triggered}:
|
||||
- order_line[0].name == u'[LCD17] 17\u201d LCD Monitor'
|
||||
- order_line[0].price_unit == 1350.0 * 12
|
||||
- order_line[0].name == u'[A8767] Apple In-Ear Headphones'
|
||||
- order_line[0].price_unit == 79.0 * 12
|
||||
- order_line[0].product_uom.id == ref('product.product_uom_dozen')
|
||||
- order_line[0].product_uom_qty == 16
|
||||
@@ -44,8 +44,6 @@ def auth_method_public():
|
||||
http.auth_methods['public'] = auth_method_public
|
||||
|
||||
NOPE = object()
|
||||
# PIL images have a type flag, but no MIME. Reverse type flag to MIME.
|
||||
PIL_MIME_MAPPING = {'PNG': 'image/png', 'JPEG': 'image/jpeg', 'GIF': 'image/gif', }
|
||||
# Completely arbitrary limits
|
||||
MAX_IMAGE_WIDTH, MAX_IMAGE_HEIGHT = IMAGE_LIMITS = (1024, 768)
|
||||
class Website(openerp.addons.web.controllers.main.Home):
|
||||
@@ -279,6 +277,15 @@ class Website(openerp.addons.web.controllers.main.Home):
|
||||
return request.website.render('website.sitemap', {'pages': request.website.list_pages()})
|
||||
|
||||
class Images(http.Controller):
|
||||
def placeholder(self, response):
|
||||
# file_open may return a StringIO. StringIO can be closed but are
|
||||
# not context managers in Python 2 though that is fixed in 3
|
||||
with contextlib.closing(openerp.tools.misc.file_open(
|
||||
os.path.join('web', 'static', 'src', 'img', 'placeholder.png'),
|
||||
mode='rb')) as f:
|
||||
response.set_data(f.read())
|
||||
return response.make_conditional(request.httprequest)
|
||||
|
||||
@website.route('/website/image', auth="public")
|
||||
def image(self, model, id, field):
|
||||
Model = request.registry[model]
|
||||
@@ -293,13 +300,7 @@ class Images(http.Controller):
|
||||
[('id', '=', id), ('website_published', '=', True)], context=request.context)
|
||||
|
||||
if not ids:
|
||||
# file_open may return a StringIO. StringIO can be closed but are
|
||||
# not context managers in Python 2 though that is fixed in 3
|
||||
with contextlib.closing(openerp.tools.misc.file_open(
|
||||
os.path.join('web', 'static', 'src', 'img', 'placeholder.png'),
|
||||
mode='rb')) as f:
|
||||
response.set_data(f.read())
|
||||
return response
|
||||
return self.placeholder(response)
|
||||
|
||||
concurrency = '__last_update'
|
||||
[record] = Model.read(request.cr, openerp.SUPERUSER_ID, [id],
|
||||
@@ -314,9 +315,11 @@ class Images(http.Controller):
|
||||
# just in case we have a timestamp without microseconds
|
||||
response.last_modified = datetime.datetime.strptime(
|
||||
record[concurrency], server_format)
|
||||
# FIXME: no field in record?
|
||||
if not field in record or not record[field]:
|
||||
return response
|
||||
|
||||
if not record.get(field):
|
||||
# Field does not exist on model or field set to False
|
||||
# FIXME: maybe a field which does not exist should be a 404?
|
||||
return self.placeholder(response)
|
||||
|
||||
response.set_etag(hashlib.sha1(record[field]).hexdigest())
|
||||
response.make_conditional(request.httprequest)
|
||||
@@ -350,7 +353,7 @@ class Images(http.Controller):
|
||||
|
||||
# FIXME: unknown format or not an image
|
||||
image = Image.open(buf)
|
||||
response.mimetype = PIL_MIME_MAPPING[image.format]
|
||||
response.mimetype = Image.MIME[image.format]
|
||||
|
||||
w, h = image.size
|
||||
max_w, max_h = fit
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import ir_fields
|
||||
import ir_ui_view
|
||||
import website
|
||||
import ir_rule
|
||||
import ir_qweb
|
||||
|
||||
import test_models
|
||||
|
||||
@@ -1,87 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from lxml import etree, html
|
||||
|
||||
from openerp.osv import orm, fields
|
||||
from openerp.tools import ustr
|
||||
|
||||
class converter(orm.Model):
|
||||
_inherit = 'ir.fields.converter'
|
||||
|
||||
def _html_to_integer(self, cr, uid, model, column, value, context=None):
|
||||
return int(value.text_content().strip()), []
|
||||
|
||||
def _html_to_float(self, cr, uid, model, column, value, context=None):
|
||||
return float(value.text_content().strip()), []
|
||||
|
||||
def _html_to_passthrough(self, cr, uid, model, column, value, context=None):
|
||||
return value.text_content().strip(), []
|
||||
|
||||
_html_to_char = _html_to_date = _html_to_datetime = _html_to_passthrough
|
||||
|
||||
def _html_to_text(self, cr, uid, model, column, value, context=None):
|
||||
return value.text_content(), []
|
||||
|
||||
def _html_to_selection(self, cr, uid, model, column, value, context=None):
|
||||
text = value.text_content().strip()
|
||||
|
||||
selection = column.reify(cr, uid, model, column, context=context)
|
||||
for k, v in selection:
|
||||
if isinstance(v, str):
|
||||
v = ustr(v)
|
||||
if text == v:
|
||||
return k, []
|
||||
|
||||
warning = u"No value found for label %s in selection %s" % (text, selection)
|
||||
# FIXME: ?
|
||||
return False, [Warning(warning.encode('utf-8'))]
|
||||
|
||||
def _html_to_many2one(self, cr, uid, model, column, value, context=None):
|
||||
matches = self.pool[column._obj].name_search(
|
||||
cr, uid, name=value.text_content().strip(), context=context)
|
||||
# FIXME: more than one match, error reporting
|
||||
return matches[0][0], []
|
||||
|
||||
def _html_to_html(self, cr, uid, model, column, value, context=None):
|
||||
content = []
|
||||
if value.text: content.append(value.text)
|
||||
content.extend(html.tostring(child)
|
||||
for child in value.iterchildren(tag=etree.Element))
|
||||
return '\n'.join(content), []
|
||||
|
||||
|
||||
class test_converter(orm.Model):
|
||||
_name = 'website.converter.test'
|
||||
|
||||
_columns = {
|
||||
'char': fields.char(),
|
||||
'integer': fields.integer(),
|
||||
'float': fields.float(),
|
||||
'numeric': fields.float(digits=(16, 2)),
|
||||
'many2one': fields.many2one('website.converter.test.sub'),
|
||||
'binary': fields.binary(),
|
||||
'date': fields.date(),
|
||||
'datetime': fields.datetime(),
|
||||
'selection': fields.selection([
|
||||
(1, "réponse A"),
|
||||
(2, "réponse B"),
|
||||
(3, "réponse C"),
|
||||
(4, "réponse D"),
|
||||
]),
|
||||
'selection_str': fields.selection([
|
||||
('A', "Qu'il n'est pas arrivé à Toronto"),
|
||||
('B', "Qu'il était supposé arriver à Toronto"),
|
||||
('C', "Qu'est-ce qu'il fout ce maudit pancake, tabernacle ?"),
|
||||
('D', "La réponse D"),
|
||||
], string="Lorsqu'un pancake prend l'avion à destination de Toronto et "
|
||||
"qu'il fait une escale technique à St Claude, on dit:"),
|
||||
'html': fields.html(),
|
||||
'text': fields.text(),
|
||||
}
|
||||
|
||||
|
||||
class test_converter_sub(orm.Model):
|
||||
_name = 'website.converter.test.sub'
|
||||
|
||||
_columns = {
|
||||
'name': fields.char(),
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Website-context rendering needs to add some metadata to rendered fields,
|
||||
as well as render a few fields differently.
|
||||
|
||||
Also, adds methods to convert values back to openerp models.
|
||||
"""
|
||||
|
||||
import cStringIO
|
||||
import itertools
|
||||
import logging
|
||||
import re
|
||||
import urllib2
|
||||
|
||||
import werkzeug.utils
|
||||
from lxml import etree, html
|
||||
from PIL import Image as I
|
||||
|
||||
from openerp.osv import orm, fields
|
||||
from openerp.tools import ustr
|
||||
|
||||
REMOTE_CONNECTION_TIMEOUT = 2.5
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class QWeb(orm.AbstractModel):
|
||||
""" QWeb object for rendering stuff in the website context
|
||||
"""
|
||||
_name = 'website.qweb'
|
||||
_inherit = 'ir.qweb'
|
||||
|
||||
def get_converter_for(self, field_type):
|
||||
return self.pool.get(
|
||||
'website.qweb.field.' + field_type,
|
||||
self.pool['website.qweb.field'])
|
||||
|
||||
class Field(orm.AbstractModel):
|
||||
_name = 'website.qweb.field'
|
||||
_inherit = 'ir.qweb.field'
|
||||
|
||||
def attributes(self, cr, uid, field_name, record, options,
|
||||
source_element, g_att, t_att, qweb_context):
|
||||
column = record._model._all_columns[field_name].column
|
||||
return itertools.chain(
|
||||
super(Field, self).attributes(cr, uid, field_name, record, options,
|
||||
source_element, g_att, t_att,
|
||||
qweb_context),
|
||||
[('data-oe-translate', 1 if column.translate else 0)]
|
||||
)
|
||||
|
||||
def value_from_string(self, value):
|
||||
return value
|
||||
|
||||
def from_html(self, cr, uid, model, column, element, context=None):
|
||||
return self.value_from_string(element.text_content().strip())
|
||||
|
||||
def qweb_object(self):
|
||||
return self.pool['website.qweb']
|
||||
|
||||
class Integer(orm.AbstractModel):
|
||||
_name = 'website.qweb.field.integer'
|
||||
_inherit = ['website.qweb.field']
|
||||
|
||||
value_from_string = int
|
||||
|
||||
class Float(orm.AbstractModel):
|
||||
_name = 'website.qweb.field.float'
|
||||
_inherit = ['website.qweb.field', 'ir.qweb.field.float']
|
||||
|
||||
value_from_string = float
|
||||
|
||||
class Text(orm.AbstractModel):
|
||||
_name = 'website.qweb.field.text'
|
||||
_inherit = ['website.qweb.field', 'ir.qweb.field.text']
|
||||
|
||||
def from_html(self, cr, uid, model, column, element, context=None):
|
||||
return element.text_content()
|
||||
|
||||
class Selection(orm.AbstractModel):
|
||||
_name = 'website.qweb.field.selection'
|
||||
_inherit = ['website.qweb.field', 'ir.qweb.field.selection']
|
||||
|
||||
def from_html(self, cr, uid, model, column, element, context=None):
|
||||
value = element.text_content().strip()
|
||||
selection = column.reify(cr, uid, model, column, context=context)
|
||||
for k, v in selection:
|
||||
if isinstance(v, str):
|
||||
v = ustr(v)
|
||||
if value == v:
|
||||
return k
|
||||
|
||||
raise ValueError(u"No value found for label %s in selection %s" % (
|
||||
value, selection))
|
||||
|
||||
class ManyToOne(orm.AbstractModel):
|
||||
_name = 'website.qweb.field.many2one'
|
||||
_inherit = ['website.qweb.field', 'ir.qweb.field.many2one']
|
||||
|
||||
def from_html(self, cr, uid, model, column, element, context=None):
|
||||
# FIXME: this behavior is really weird, what if the user wanted to edit the name of the related thingy? Should m2os really be editable without a widget?
|
||||
matches = self.pool[column._obj].name_search(
|
||||
cr, uid, name=element.text_content().strip(), context=context)
|
||||
# FIXME: no match? More than 1 match?
|
||||
assert len(matches) == 1
|
||||
return matches[0][0]
|
||||
|
||||
class HTML(orm.AbstractModel):
|
||||
_name = 'website.qweb.field.html'
|
||||
_inherit = ['website.qweb.field', 'ir.qweb.field.html']
|
||||
|
||||
def from_html(self, cr, uid, model, column, element, context=None):
|
||||
content = []
|
||||
if element.text: content.append(element.text)
|
||||
content.extend(html.tostring(child)
|
||||
for child in element.iterchildren(tag=etree.Element))
|
||||
return '\n'.join(content)
|
||||
|
||||
|
||||
class Image(orm.AbstractModel):
|
||||
"""
|
||||
Widget options:
|
||||
|
||||
``class``
|
||||
set as attribute on the generated <img> tag
|
||||
"""
|
||||
_name = 'website.qweb.field.image'
|
||||
_inherit = ['website.qweb.field', 'ir.qweb.field.image']
|
||||
|
||||
def to_html(self, cr, uid, field_name, record, options,
|
||||
source_element, t_att, g_att, qweb_context):
|
||||
assert source_element.nodeName != 'img',\
|
||||
"Oddly enough, the root tag of an image field can not be img. " \
|
||||
"That is because the image goes into the tag, or it gets the " \
|
||||
"hose again."
|
||||
|
||||
return super(Image, self).to_html(
|
||||
cr, uid, field_name, record, options,
|
||||
source_element, t_att, g_att, qweb_context)
|
||||
|
||||
def record_to_html(self, cr, uid, field_name, record, column, options=None):
|
||||
cls = ''
|
||||
if 'class' in options:
|
||||
cls = ' class="%s"' % werkzeug.utils.escape(options['class'])
|
||||
|
||||
return '<img%s src="/website/image?model=%s&field=%s&id=%s"/>' % (
|
||||
cls, record._model._name, field_name, record.id)
|
||||
|
||||
def from_html(self, cr, uid, model, column, element, context=None):
|
||||
url = element.find('img').get('src')
|
||||
|
||||
m = re.match(r'^/website/attachment/(\d+)$', url)
|
||||
if m:
|
||||
attachment = self.pool['ir.attachment'].browse(
|
||||
cr, uid, int(m.group(1)), context=context)
|
||||
return attachment.datas
|
||||
|
||||
# remote URL?
|
||||
try:
|
||||
# should probably remove remote URLs entirely:
|
||||
# * in fields, downloading them without blowing up the server is a
|
||||
# challenge
|
||||
# * in views, may trigger mixed content warnings if HTTPS CMS
|
||||
# linking to HTTP images
|
||||
# implement drag & drop image upload to mitigate?
|
||||
|
||||
req = urllib2.urlopen(url, timeout=REMOTE_CONNECTION_TIMEOUT)
|
||||
# PIL needs a seekable file-like image, urllib result is not seekable
|
||||
image = I.open(cStringIO.StringIO(req.read()))
|
||||
# force a complete load of the image data to validate it
|
||||
image.load()
|
||||
except Exception:
|
||||
logger.exception("Failed to load remote image %r", url)
|
||||
return False
|
||||
|
||||
# don't use original data in case weird stuff was smuggled in, with
|
||||
# luck PIL will remove some of it?
|
||||
out = cStringIO.StringIO()
|
||||
image.save(out, image.format)
|
||||
return out.getvalue().encode('base64')
|
||||
|
||||
class Monetary(orm.AbstractModel):
|
||||
_name = 'website.qweb.field.monetary'
|
||||
_inherit = ['website.qweb.field', 'ir.qweb.field.monetary']
|
||||
|
||||
def from_html(self, cr, uid, model, column, element, context=None):
|
||||
return float(element.find('span').text)
|
||||
@@ -10,6 +10,9 @@ class view(osv.osv):
|
||||
'inherit_option_id': fields.many2one('ir.ui.view','Optional Inheritancy'),
|
||||
'inherited_option_ids': fields.one2many('ir.ui.view','inherit_option_id','Optional Inheritancies'),
|
||||
'page': fields.boolean("Whether this view is a web page template (complete)"),
|
||||
'website_meta_title': fields.char("Website meta title", size=70, translate=True),
|
||||
'website_meta_description': fields.text("Website meta description", translate=True),
|
||||
'website_meta_keywords': fields.char("Website meta keywords", size=70, translate=True),
|
||||
}
|
||||
_defaults = {
|
||||
'page': False,
|
||||
@@ -60,11 +63,9 @@ class view(osv.osv):
|
||||
field = el.get('data-oe-field')
|
||||
|
||||
column = Model._all_columns[field].column
|
||||
convert = self.pool['ir.fields.converter'].to_field(
|
||||
cr, uid, Model, column, fromtype="html", context=context)
|
||||
value, warnings = convert(el)
|
||||
# FIXME: report error
|
||||
if warnings: return
|
||||
converter = self.pool['website.qweb'].get_converter_for(
|
||||
el.get('data-oe-type'))
|
||||
value = converter.from_html(cr, uid, Model, column, el)
|
||||
|
||||
Model.write(cr, uid, [int(el.get('data-oe-id'))], {
|
||||
field: value
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from openerp.osv import orm, fields
|
||||
|
||||
class test_converter(orm.Model):
|
||||
_name = 'website.converter.test'
|
||||
|
||||
_columns = {
|
||||
'char': fields.char(),
|
||||
'integer': fields.integer(),
|
||||
'float': fields.float(),
|
||||
'numeric': fields.float(digits=(16, 2)),
|
||||
'many2one': fields.many2one('website.converter.test.sub'),
|
||||
'binary': fields.binary(),
|
||||
'date': fields.date(),
|
||||
'datetime': fields.datetime(),
|
||||
'selection': fields.selection([
|
||||
(1, "réponse A"),
|
||||
(2, "réponse B"),
|
||||
(3, "réponse C"),
|
||||
(4, "réponse D"),
|
||||
]),
|
||||
'selection_str': fields.selection([
|
||||
('A', "Qu'il n'est pas arrivé à Toronto"),
|
||||
('B', "Qu'il était supposé arriver à Toronto"),
|
||||
('C', "Qu'est-ce qu'il fout ce maudit pancake, tabernacle ?"),
|
||||
('D', "La réponse D"),
|
||||
], string="Lorsqu'un pancake prend l'avion à destination de Toronto et "
|
||||
"qu'il fait une escale technique à St Claude, on dit:"),
|
||||
'html': fields.html(),
|
||||
'text': fields.text(),
|
||||
}
|
||||
|
||||
|
||||
class test_converter_sub(orm.Model):
|
||||
_name = 'website.converter.test.sub'
|
||||
|
||||
_columns = {
|
||||
'name': fields.char(),
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import fnmatch
|
||||
import functools
|
||||
import simplejson
|
||||
|
||||
@@ -49,7 +50,7 @@ def auth_method_public():
|
||||
request.uid = request.session.uid
|
||||
http.auth_methods['public'] = auth_method_public
|
||||
|
||||
def url_for(path, lang=None):
|
||||
def url_for(path, lang=None, keep_query=None):
|
||||
if request:
|
||||
path = urljoin(request.httprequest.path, path)
|
||||
langs = request.context.get('langs')
|
||||
@@ -61,6 +62,16 @@ def url_for(path, lang=None):
|
||||
else:
|
||||
ps.insert(1, lang)
|
||||
path = '/'.join(ps)
|
||||
if keep_query:
|
||||
keep = []
|
||||
params = werkzeug.url_decode(request.httprequest.query_string)
|
||||
params_keys = tuple(params.keys())
|
||||
for kq in keep_query:
|
||||
keep += fnmatch.filter(params_keys, kq)
|
||||
if keep:
|
||||
params = dict([(k, params[k]) for k in keep])
|
||||
path += u'?%s' % werkzeug.urls.url_encode(params)
|
||||
|
||||
return path
|
||||
|
||||
def urlplus(url, params):
|
||||
@@ -157,21 +168,29 @@ class website(osv.osv):
|
||||
inherit_branding=qweb_context.setdefault('editable', False),
|
||||
)
|
||||
|
||||
view_ref = None
|
||||
# check if xmlid of the template exists
|
||||
try:
|
||||
module, xmlid = template.split('.', 1)
|
||||
IMD.get_object_reference(cr, uid, module, xmlid)
|
||||
view_ref = IMD.get_object_reference(cr, uid, module, xmlid)
|
||||
except ValueError: # catches both unpack errors and gor errors
|
||||
module, xmlid = 'website', template
|
||||
try:
|
||||
IMD.get_object_reference(cr, uid, module, xmlid)
|
||||
view_ref = IMD.get_object_reference(cr, uid, module, xmlid)
|
||||
except ValueError:
|
||||
logger.error("Website Rendering Error.\n\n%s" % traceback.format_exc())
|
||||
return self.render(cr, uid, ids, 'website.404', qweb_context)
|
||||
|
||||
try:
|
||||
return view.render(cr, uid, "%s.%s" % (module, xmlid),
|
||||
qweb_context, context=context)
|
||||
main_object = request.registry[view_ref[0]].browse(cr, uid, view_ref[1])
|
||||
qweb_context['main_object'] = main_object
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
try:
|
||||
return view.render(
|
||||
cr, uid, "%s.%s" % (module, xmlid), qweb_context,
|
||||
engine='website.qweb', context=context)
|
||||
except (AccessError, AccessDenied), err:
|
||||
logger.error(err)
|
||||
qweb_context['error'] = err[1]
|
||||
|
||||
@@ -1,3 +1,14 @@
|
||||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||
access_website_public,website,website.model_website,base.group_public,1,0,0,0
|
||||
access_website,website,website.model_website,base.group_user,1,0,0,0
|
||||
access_website,website,website.model_website,base.group_user,1,0,0,0
|
||||
access_website_converter_test,access_website_converter_test,model_website_converter_test,,1,1,1,1
|
||||
access_website_converter_test_sub,access_website_converter_test_sub,model_website_converter_test_sub,,1,1,1,1
|
||||
access_website_qweb,access_website_qweb,model_website_qweb,,0,0,0,0
|
||||
access_website_qweb_field,access_website_qweb_field,model_website_qweb_field,,0,0,0,0
|
||||
access_website_qweb_field_float,access_website_qweb_field_float,model_website_qweb_field_float,,0,0,0,0
|
||||
access_website_qweb_field_text,access_website_qweb_field_text,model_website_qweb_field_text,,0,0,0,0
|
||||
access_website_qweb_field_selection,access_website_qweb_field_selection,model_website_qweb_field_selection,,0,0,0,0
|
||||
access_website_qweb_field_many2one,access_website_qweb_field_many2one,model_website_qweb_field_many2one,,0,0,0,0
|
||||
access_website_qweb_field_html,access_website_qweb_field_html,model_website_qweb_field_html,,0,0,0,0
|
||||
access_website_qweb_field_image,access_website_qweb_field_image,model_website_qweb_field_image,,0,0,0,0
|
||||
access_website_qweb_field_monetary,access_website_qweb_field_monetary,model_website_qweb_field_monetary,,0,0,0,0
|
||||
|
||||
|
@@ -580,8 +580,10 @@
|
||||
// Prevent the browser opening read-only links. (#6032)
|
||||
this.attachListener( this, 'click', function( ev ) {
|
||||
ev = ev.data;
|
||||
var target = ev.getTarget();
|
||||
if ( target.is( 'a' ) && ev.$.button != 2 && target.isReadOnly() )
|
||||
var target = ev.getTarget(),
|
||||
link = new CKEDITOR.dom.elementPath( target ).contains( 'a' );
|
||||
|
||||
if ( link && ev.$.button != 2 && link.isReadOnly() )
|
||||
ev.preventDefault();
|
||||
} );
|
||||
|
||||
|
||||
@@ -155,10 +155,19 @@ table.editorbar-panel td.selected {
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
.cke_widget_wrapper {
|
||||
position: static !important;
|
||||
}
|
||||
|
||||
.cke_widget_drag_handler_container {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.cke_widget_editable:empty:after {
|
||||
content: " ";
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
/* ---- SNIPPET EDITOR ---- */
|
||||
#oe_snippets {
|
||||
position: fixed;
|
||||
|
||||
@@ -138,9 +138,18 @@ table.editorbar-panel
|
||||
.modal .image-preview
|
||||
margin-bottom: 0.5em
|
||||
|
||||
// wrapper positioned relatively for drag&drop widget which is disabled below.
|
||||
// Breaks completely horribly crazy products listing page, so take it out.
|
||||
.cke_widget_wrapper
|
||||
position: static !important
|
||||
// "remove" drag & drop of CKE widgets
|
||||
.cke_widget_drag_handler_container
|
||||
display: none !important
|
||||
// prevent inline widgets from entirely disappearing when their (textual)
|
||||
// content is removed
|
||||
.cke_widget_editable:empty:after
|
||||
content: " "
|
||||
white-space: pre-wrap
|
||||
|
||||
/* ---- SNIPPET EDITOR ---- */
|
||||
|
||||
|
||||
@@ -160,9 +160,17 @@
|
||||
editables: { text: '*' },
|
||||
|
||||
upcast: function (el) {
|
||||
return el.attributes['data-oe-type'];
|
||||
return el.attributes['data-oe-type']
|
||||
&& el.attributes['data-oe-type'] !== 'monetary';
|
||||
},
|
||||
});
|
||||
editor.widgets.add('monetary', {
|
||||
editables: { text: 'span.oe_currency_value' },
|
||||
|
||||
upcast: function (el) {
|
||||
return el.attributes['data-oe-type'] === 'monetary';
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -345,12 +353,17 @@
|
||||
var self = this;
|
||||
// setup dirty-marking for each editable element
|
||||
this.fetch_editables(root)
|
||||
.prop('contentEditable', true)
|
||||
.addClass('oe_editable')
|
||||
.each(function () {
|
||||
var node = this;
|
||||
observer.observe(node, OBSERVER_CONFIG);
|
||||
var $node = $(node);
|
||||
// only explicitly set contenteditable on view sections,
|
||||
// cke widgets system will do the widgets themselves
|
||||
if ($node.data('oe-model') === 'ir.ui.view') {
|
||||
node.contentEditable = true;
|
||||
}
|
||||
|
||||
observer.observe(node, OBSERVER_CONFIG);
|
||||
$node.one('content_changed', function () {
|
||||
$node.addClass('oe_dirty');
|
||||
self.trigger('change');
|
||||
@@ -470,7 +483,7 @@
|
||||
},
|
||||
start: function () {
|
||||
var sup = this._super();
|
||||
this.$el.modal();
|
||||
this.$el.modal({backdrop: 'static'});
|
||||
return sup;
|
||||
},
|
||||
save: function () {
|
||||
@@ -812,6 +825,7 @@
|
||||
|
||||
var style = data.style;
|
||||
element.setAttribute('src', data.url);
|
||||
element.removeAttribute('data-cke-saved-src');
|
||||
$(element.$).removeClass(this.image_styles.join(' '));
|
||||
if (style) { element.addClass(style); }
|
||||
},
|
||||
|
||||
@@ -109,9 +109,13 @@
|
||||
website.ready = function() {
|
||||
if (!all_ready) {
|
||||
var tpl = website.load_templates(templates);
|
||||
// var session;
|
||||
// var trads = openerp._t.database.load_translations(session, ['website'], website.get_context().lang);
|
||||
all_ready = $.when(dom_ready, tpl);
|
||||
all_ready = dom_ready.then(function () {
|
||||
if ($('html').data('editable')) {
|
||||
website.session = new openerp.Session();
|
||||
var modules = ['website'];
|
||||
return openerp._t.database.load_translations(website.session, modules, website.get_context().lang);
|
||||
}
|
||||
}).then(tpl).promise();
|
||||
}
|
||||
return all_ready;
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
|
||||
var website = openerp.website;
|
||||
website.templates.push('/website/static/src/xml/website.seo.xml');
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
'click a[data-action=promote-current-page]': 'launchSeo',
|
||||
}),
|
||||
launchSeo: function () {
|
||||
(new website.seo.Configurator()).appendTo($(document.body));
|
||||
(new website.seo.Configurator(this)).appendTo($(document.body));
|
||||
},
|
||||
});
|
||||
|
||||
@@ -241,24 +241,29 @@
|
||||
return hashIndex >= 0 ? url.substring(0, hashIndex) : url;
|
||||
},
|
||||
title: function () {
|
||||
return $('title').text().trim();
|
||||
return ($('title').length > 0) && $('title').text() && $('title').text().trim();
|
||||
},
|
||||
changeTitle: function (title) {
|
||||
// TODO create tag if missing
|
||||
$('title').text(title);
|
||||
this.trigger('title-changed', title);
|
||||
},
|
||||
description: function () {
|
||||
return $('meta[name=description]').attr('value').trim();
|
||||
var $description = $('meta[name=description]');
|
||||
return ($description.length > 0) && $description.attr('value') && $description.attr('value').trim();
|
||||
},
|
||||
changeDescription: function (description) {
|
||||
// TODO create tag if missing
|
||||
$('meta[name=description]').attr('value', description);
|
||||
this.trigger('description-changed', description);
|
||||
},
|
||||
keywords: function () {
|
||||
var parsed = $('meta[name=keywords]').attr('value').split(",");
|
||||
return parsed[0] ? parsed: [];
|
||||
var $keywords = $('meta[name=keywords]');
|
||||
var parsed = ($keywords.length > 0) && $keywords.attr('value') && $keywords.attr('value').split(",");
|
||||
return (parsed && parsed[0]) ? parsed: [];
|
||||
},
|
||||
changeKeywords: function (keywords) {
|
||||
// TODO create tag if missing
|
||||
$('meta[name=keywords]').attr('value', keywords.join(","));
|
||||
this.trigger('keywords-changed', keywords);
|
||||
},
|
||||
@@ -364,7 +369,8 @@
|
||||
}
|
||||
var htmlPage = this.htmlPage;
|
||||
|
||||
// desactivated as too complex for end-users
|
||||
// Add message suggestions at the top of the dialog
|
||||
// if necessary....
|
||||
// if (htmlPage.headers('h1').length === 0) {
|
||||
// tips.push({
|
||||
// type: 'warning',
|
||||
@@ -390,15 +396,60 @@
|
||||
$input.val("");
|
||||
},
|
||||
update: function () {
|
||||
var self = this;
|
||||
var data = {
|
||||
title: this.htmlPage.title(),
|
||||
description: this.htmlPage.description(),
|
||||
keywords: this.keywordList.keywords(),
|
||||
images: this.imageList.images(),
|
||||
website_meta_title: self.htmlPage.title(),
|
||||
website_meta_description: self.htmlPage.description(),
|
||||
website_meta_keywords: self.keywordList.keywords().join(", "),
|
||||
};
|
||||
console.log(data);
|
||||
// TODO Persist changes
|
||||
this.$el.modal('hide');
|
||||
self.saveMetaData(data).then(function () {
|
||||
self.$el.modal('hide');
|
||||
});
|
||||
},
|
||||
getMainObject: function () {
|
||||
var repr = $('html').data('main-object');
|
||||
var m = repr.match(/.+\((.+), (\d+)\)/);
|
||||
if (!m) {
|
||||
return null;
|
||||
} else {
|
||||
return {
|
||||
model: m[1],
|
||||
id: m[2]|0
|
||||
};
|
||||
}
|
||||
},
|
||||
loadMetaData: function () {
|
||||
var self = this;
|
||||
var obj = this.getMainObject();
|
||||
var def = $.Deferred();
|
||||
if (!obj) {
|
||||
// return $.Deferred().reject(new Error("No main_object was found."));
|
||||
def.resolve(null);
|
||||
} else {
|
||||
var fields = ['website_meta_title', 'website_meta_description', 'website_meta_keywords'];
|
||||
var model = website.session.model(obj.model);
|
||||
model.call('read', [[obj.id], fields, website.get_context()]).then(function (data) {
|
||||
if (data.length) {
|
||||
var meta = data[0];
|
||||
meta.model = obj.model;
|
||||
def.resolve(meta);
|
||||
} else {
|
||||
def.resolve(null);
|
||||
}
|
||||
}).fail(function () {
|
||||
def.reject();
|
||||
});
|
||||
}
|
||||
return def;
|
||||
},
|
||||
saveMetaData: function (data) {
|
||||
var obj = this.getMainObject();
|
||||
if (!obj) {
|
||||
return $.Deferred().reject();
|
||||
} else {
|
||||
var model = website.session.model(obj.model);
|
||||
return model.call('write', [[obj.id], data, website.get_context()]);
|
||||
}
|
||||
},
|
||||
titleChanged: function () {
|
||||
var self = this;
|
||||
|
||||
@@ -1029,6 +1029,10 @@
|
||||
self.$target.carousel();
|
||||
});
|
||||
|
||||
this.$target.on('dblclick', '.item.active .carousel-image img', function (event) {
|
||||
$('#oe_rte_toolbar .cke_button__image .cke_button__image_icon').click();
|
||||
});
|
||||
|
||||
this.rebind_event();
|
||||
},
|
||||
// rebind event to active carousel on edit mode
|
||||
|
||||
@@ -124,7 +124,7 @@
|
||||
},
|
||||
{
|
||||
stepId: 'drag-banner',
|
||||
element: '#website-top-navbar [data-snippet-id=carousel]',
|
||||
element: '#website-top-navbar [data-snippet-id=carousel].ui-draggable',
|
||||
placement: 'bottom',
|
||||
title: "Drag & Drop a Banner",
|
||||
content: "Drag the <em>Banner</em> block and drop it in your page. <p class='text-muted'>Tip: release the mouse button when you are in a valid zone, with a preview of the banner.</p>",
|
||||
@@ -238,7 +238,7 @@
|
||||
$menuItem.click(function () {
|
||||
tutorial.reset();
|
||||
tutorial.start();
|
||||
})
|
||||
});
|
||||
menu.append($menuItem);
|
||||
});
|
||||
var url = new website.UrlParser(window.location.href);
|
||||
|
||||
@@ -1,31 +1,19 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from collections import namedtuple
|
||||
from functools import partial
|
||||
from xml.dom.minidom import getDOMImplementation
|
||||
|
||||
from lxml import etree, html
|
||||
from lxml import html
|
||||
|
||||
from openerp.tests import common
|
||||
from openerp.tools import qweb
|
||||
import unittest2
|
||||
from openerp.addons.base.ir import ir_qweb
|
||||
|
||||
renderer = qweb.QWebXml()
|
||||
impl = getDOMImplementation()
|
||||
document = impl.createDocument(None, None, None)
|
||||
|
||||
Request = namedtuple('Request', 'cr uid registry')
|
||||
class RegistryProxy(object):
|
||||
def __init__(self, func):
|
||||
self.func = func
|
||||
def __getitem__(self, name):
|
||||
return self.func(name)
|
||||
|
||||
class TestConvertBack(common.TransactionCase):
|
||||
def setUp(self):
|
||||
super(TestConvertBack, self).setUp()
|
||||
|
||||
self.Converter = self.registry('ir.fields.converter')
|
||||
|
||||
def field_rountrip_result(self, field, value, expected):
|
||||
model = 'website.converter.test'
|
||||
Model = self.registry(model)
|
||||
@@ -39,21 +27,19 @@ class TestConvertBack(common.TransactionCase):
|
||||
field_value = 'record.%s' % field
|
||||
e.setAttribute('t-field', field_value)
|
||||
|
||||
rendered = renderer.render_tag_field(
|
||||
e, {'field': field_value}, '', {
|
||||
rendered = self.registry('website.qweb').render_tag_field(
|
||||
e, {'field': field_value}, '', ir_qweb.QWebContext({
|
||||
'record': record,
|
||||
'_request': Request(self.cr, self.uid, RegistryProxy( self.registry)),
|
||||
})
|
||||
}))
|
||||
element = html.fromstring(
|
||||
rendered, parser=html.HTMLParser(encoding='utf-8'))
|
||||
|
||||
column = Model._all_columns[field].column
|
||||
converter = self.registry('website.qweb').get_converter_for(
|
||||
element.get('data-oe-type'))
|
||||
|
||||
from_html = self.Converter.to_field(
|
||||
self.cr, self.uid, model, column, 'html')
|
||||
|
||||
value_back, warnings = from_html(element)
|
||||
self.assertEqual(warnings, [])
|
||||
value_back = converter.from_html(
|
||||
self.cr, self.uid, model, column, element)
|
||||
|
||||
if isinstance(expected, str):
|
||||
expected = expected.decode('utf-8')
|
||||
|
||||
@@ -31,8 +31,8 @@ class TestViewSaving(common.TransactionCase):
|
||||
h.H3("Column 2"),
|
||||
h.UL(
|
||||
h.LI("Item 1"),
|
||||
h.LI(h.SPAN("My Company", attrs(model='res.company', id=1, field='name'))),
|
||||
h.LI(h.SPAN("+00 00 000 00 0 000", attrs(model='res.company', id=1, field='phone')))
|
||||
h.LI(h.SPAN("My Company", attrs(model='res.company', id=1, field='name', type='char'))),
|
||||
h.LI(h.SPAN("+00 00 000 00 0 000", attrs(model='res.company', id=1, field='phone', type='char')))
|
||||
))
|
||||
)
|
||||
self.view_id = self.registry('ir.ui.view').create(self.cr, self.uid, {
|
||||
@@ -46,14 +46,15 @@ class TestViewSaving(common.TransactionCase):
|
||||
self.cr, self.uid, self.arch, context=None)
|
||||
|
||||
expect = [
|
||||
h.SPAN("My Company", attrs(model='res.company', id=1, field='name')),
|
||||
h.SPAN("+00 00 000 00 0 000", attrs(model='res.company', id=1, field='phone')),
|
||||
h.SPAN("My Company", attrs(model='res.company', id=1, field='name', type='char')),
|
||||
h.SPAN("+00 00 000 00 0 000", attrs(model='res.company', id=1, field='phone', type='char')),
|
||||
]
|
||||
for actual, expected in itertools.izip_longest(fields, expect):
|
||||
self.eq(actual, expected)
|
||||
|
||||
def test_embedded_save(self):
|
||||
embedded = h.SPAN("+00 00 000 00 0 000", attrs(model='res.company', id=1, field='phone'))
|
||||
embedded = h.SPAN("+00 00 000 00 0 000", attrs(
|
||||
model='res.company', id=1, field='phone', type='char'))
|
||||
|
||||
self.registry('ir.ui.view').save_embedded_field(self.cr, self.uid, embedded)
|
||||
|
||||
@@ -118,8 +119,8 @@ class TestViewSaving(common.TransactionCase):
|
||||
h.H3("Column 2"),
|
||||
h.UL(
|
||||
h.LI("Item 1"),
|
||||
h.LI(h.SPAN("My Company", attrs(model='res.company', id=1, field='name'))),
|
||||
h.LI(h.SPAN("+00 00 000 00 0 000", attrs(model='res.company', id=1, field='phone')))
|
||||
h.LI(h.SPAN("My Company", attrs(model='res.company', id=1, field='name', type='char'))),
|
||||
h.LI(h.SPAN("+00 00 000 00 0 000", attrs(model='res.company', id=1, field='phone', type='char')))
|
||||
))
|
||||
))
|
||||
|
||||
@@ -137,8 +138,8 @@ class TestViewSaving(common.TransactionCase):
|
||||
h.H3("Column 2"),
|
||||
h.UL(
|
||||
h.LI("wob wob wob"),
|
||||
h.LI(h.SPAN("Acme Corporation", attrs(model='res.company', id=1, field='name', expression="bob"))),
|
||||
h.LI(h.SPAN("+12 3456789", attrs(model='res.company', id=1, field='phone', expression="edmund"))),
|
||||
h.LI(h.SPAN("Acme Corporation", attrs(model='res.company', id=1, field='name', expression="bob", type='char'))),
|
||||
h.LI(h.SPAN("+12 3456789", attrs(model='res.company', id=1, field='phone', expression="edmund", type='char'))),
|
||||
)
|
||||
), encoding='utf-8')
|
||||
View.save(self.cr, self.uid, res_id=self.view_id, value=replacement,
|
||||
@@ -173,7 +174,7 @@ class TestViewSaving(common.TransactionCase):
|
||||
|
||||
node = html.tostring(h.SPAN(
|
||||
"Acme Corporation",
|
||||
attrs(model='res.company', id=company_id, field="name", expression='bob')))
|
||||
attrs(model='res.company', id=company_id, field="name", expression='bob', type='char')))
|
||||
|
||||
self.registry('ir.ui.view').save(self.cr, self.uid, res_id=company_id,value=node)
|
||||
|
||||
@@ -184,7 +185,9 @@ class TestViewSaving(common.TransactionCase):
|
||||
def test_field_tail(self):
|
||||
View = self.registry('ir.ui.view')
|
||||
replacement = ET.tostring(
|
||||
h.LI(h.SPAN("+12 3456789", attrs(model='res.company', id=1, field='phone', expression="edmund")),
|
||||
h.LI(h.SPAN("+12 3456789", attrs(
|
||||
model='res.company', id=1, type='char',
|
||||
field='phone', expression="edmund")),
|
||||
"whop whop"
|
||||
), encoding="utf-8")
|
||||
View.save(self.cr, self.uid, res_id = self.view_id, value=replacement,
|
||||
@@ -203,7 +206,7 @@ class TestViewSaving(common.TransactionCase):
|
||||
h.H3("Column 2"),
|
||||
h.UL(
|
||||
h.LI("Item 1"),
|
||||
h.LI(h.SPAN("My Company", attrs(model='res.company', id=1, field='name'))),
|
||||
h.LI(h.SPAN("My Company", attrs(model='res.company', id=1, field='name', type='char'))),
|
||||
h.LI(h.SPAN({'t-field': "edmund"}), "whop whop"),
|
||||
))
|
||||
)
|
||||
|
||||
@@ -69,8 +69,8 @@
|
||||
</ol>
|
||||
<div class="carousel-inner">
|
||||
<div class="item image_text active" style="background-image: url('/website/static/src/img/banner/color_splash.jpg')">
|
||||
<div class="container" contenteditable="true">
|
||||
<div class="carousel-caption content">
|
||||
<div class="container">
|
||||
<div class="carousel-caption content" contenteditable="true">
|
||||
<h2>Your Banner Title</h2>
|
||||
<h3>Click to customize this text</h3>
|
||||
<p>
|
||||
|
||||
@@ -13,13 +13,24 @@
|
||||
<template id="layout" name="Main layout">
|
||||
<!DOCTYPE html>
|
||||
<html t-att-lang="lang.replace('_', '-')"
|
||||
t-att-data-editable="'1' if editable else None"
|
||||
t-att-data-translatable="'1' if translatable else None"
|
||||
t-att-data-view-xmlid="xmlid if editable else None">
|
||||
t-att-data-view-xmlid="xmlid if editable else None"
|
||||
t-att-data-main-object="repr(main_object) if editable else None">
|
||||
<head>
|
||||
<title><t t-esc="title or res_company.name"/></title>
|
||||
<meta name="openerp.company" t-att-value="res_company.name" />
|
||||
<meta name="description" value="" />
|
||||
<meta name="keywords" value="" />
|
||||
<t t-if="main_object and 'website_meta_title' in main_object">
|
||||
<t t-set="title" t-value="main_object.website_meta_title"/>
|
||||
</t>
|
||||
<t t-if="not title and main_object and 'name' in main_object">
|
||||
<t t-set="additional_title" t-value="main_object.name"/>
|
||||
</t>
|
||||
<t t-if="not title">
|
||||
<t t-set="title"><t t-raw="res_company.name"/><t t-if="additional_title"> - <t t-raw="additional_title"/></t></t>
|
||||
</t>
|
||||
<title><t t-esc="title"/></title>
|
||||
<meta name="openerp.company" t-att-value="res_company.name"/>
|
||||
<meta name="description" t-att-value="main_object and main_object.website_meta_description or website_meta_description"/>
|
||||
<meta name="keywords" t-att-value="main_object and main_object.website_meta_keywords or website_meta_keywords"/>
|
||||
|
||||
<!-- Load stylesheets before scripts to avoid blocking -->
|
||||
<link rel='stylesheet' href='/web/static/lib/fontawesome/css/font-awesome.css'/>
|
||||
@@ -48,6 +59,7 @@
|
||||
<script t-if="not translatable" type="text/javascript" src="/website/static/lib/ace/ace.js"></script>
|
||||
<script type="text/javascript" src="/website/static/lib/vkbeautify/vkbeautify.0.99.00.beta.js"></script>
|
||||
<script type="text/javascript" src="/web/static/lib/jquery.ui/js/jquery-ui-1.9.1.custom.js"></script>
|
||||
<link rel='stylesheet' href="/web/static/lib/jquery.ui/css/smoothness/jquery-ui-1.9.1.custom.css"/>
|
||||
<!-- mutation observers shim backed by mutation events (8 < IE < 11, Safari < 6, FF < 14, Chrome < 17) -->
|
||||
<script type="text/javascript" src="/website/static/lib/MutationObservers/test/sidetable.js"></script>
|
||||
<script type="text/javascript" src='/website/static/lib/nearest/jquery.nearest.js'></script>
|
||||
@@ -410,9 +422,6 @@ Sitemap: <t t-esc="url_root"/>sitemap.xml
|
||||
|
||||
<template id="contactus" name="Contact us" page="True">
|
||||
<t t-call="website.layout">
|
||||
<t t-set="title">Contact us -
|
||||
<t t-raw="res_company.name"></t>
|
||||
</t>
|
||||
<div id="wrap">
|
||||
<div class="oe_structure"/>
|
||||
<div class="container">
|
||||
@@ -437,10 +446,8 @@ Sitemap: <t t-esc="url_root"/>sitemap.xml
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<template id="aboutus" page="True">
|
||||
<template id="aboutus" name="About us" page="True">
|
||||
<t t-call="website.layout">
|
||||
<t t-set="title">About <t t-raw="res_company.name"></t>
|
||||
</t>
|
||||
<div id="wrap">
|
||||
<div class="oe_structure"/>
|
||||
<div class="container mb32">
|
||||
|
||||
@@ -176,13 +176,12 @@
|
||||
</template>
|
||||
|
||||
<!-- Page -->
|
||||
<template id="index" name="Blogs" page="True">
|
||||
<template id="index" name="Blog" page="True">
|
||||
<t t-call="website.layout">
|
||||
<t t-set="head">
|
||||
<script type="text/javascript" src="/website_blog/static/src/js/website_blog.js"></script>
|
||||
<link rel='stylesheet' href='/website_blog/static/src/css/website_blog.css'/>
|
||||
</t>
|
||||
<t t-set="title">Blog</t>
|
||||
<div id="wrap">
|
||||
<div class="container mt16 js_website_blog">
|
||||
<div class="row">
|
||||
|
||||
@@ -11,7 +11,7 @@ OpenERP Contact Form
|
||||
'author': 'OpenERP SA',
|
||||
'depends': ['website_partner', 'crm'],
|
||||
'data': [
|
||||
'website_crm_data.xml',
|
||||
'data/website_crm_data.xml',
|
||||
'views/website_crm.xml',
|
||||
],
|
||||
'installable': True,
|
||||
|
||||
@@ -57,9 +57,6 @@
|
||||
|
||||
<template id="contactus_thanks" name="Contact us" page="True">
|
||||
<t t-call="website.layout">
|
||||
<t t-set="title">Contact us -
|
||||
<t t-raw="res_company.name"></t>
|
||||
</t>
|
||||
<div id="wrap">
|
||||
<div class="oe_structure"/>
|
||||
<div class="container">
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
<!-- Page -->
|
||||
<template id="layout" name="Partners Layout">
|
||||
<t t-call="website.layout">
|
||||
<t t-set="title">Partners</t>
|
||||
<t t-set="additional_title">Partners</t>
|
||||
<div id="wrap">
|
||||
<div class="oe_structure"/>
|
||||
<div class="container">
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
<!-- Page -->
|
||||
<template id="layout" name="Customer References Layout">
|
||||
<t t-call="website.layout">
|
||||
<t t-set="title">Customer References</t>
|
||||
<t t-set="additional_title">Customer References</t>
|
||||
<div id="wrap">
|
||||
<div class="oe_structure"/>
|
||||
<div class="container">
|
||||
|
||||
@@ -15,10 +15,6 @@
|
||||
<!-- Page -->
|
||||
<template id="index" name="Events" page="True">
|
||||
<t t-call="website.layout">
|
||||
<t t-set="head">
|
||||
<t t-raw="head or ''"/>
|
||||
</t>
|
||||
<t t-set="title">Events</t>
|
||||
<div id="wrap">
|
||||
<div class="container">
|
||||
<div class="oe_structure"/>
|
||||
@@ -122,10 +118,7 @@
|
||||
|
||||
<template id="event_description_full">
|
||||
<t t-call="website.layout">
|
||||
<t t-set="head">
|
||||
<t t-raw="head or ''"/>
|
||||
</t>
|
||||
<t t-set="title">Events</t>
|
||||
<t t-set="additional_title">Events</t>
|
||||
<div id="wrap">
|
||||
<div class="container">
|
||||
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
import controllers
|
||||
import hr
|
||||
import models
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
'name': 'Team Page',
|
||||
'name': 'Team Page',
|
||||
'category': 'Website',
|
||||
'summary': 'Present Your Team',
|
||||
'version': '1.0',
|
||||
@@ -11,10 +11,10 @@ Our Team Page
|
||||
'author': 'OpenERP SA',
|
||||
'depends': ['website', 'hr'],
|
||||
'demo': [
|
||||
'website_hr_demo.xml',
|
||||
'data/website_hr_demo.xml',
|
||||
],
|
||||
'data': [
|
||||
'website_hr_data.xml',
|
||||
'data/website_hr_data.xml',
|
||||
'views/website_hr.xml',
|
||||
'security/ir.model.access.csv',
|
||||
'security/website_hr.xml',
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
import hr
|
||||
@@ -11,12 +11,14 @@ OpenERP Contact Form
|
||||
'author': 'OpenERP SA',
|
||||
'depends': ['hr_recruitment', 'website_mail'],
|
||||
'data': [
|
||||
'website_hr_recruitment_demo.xml',
|
||||
'website_hr_recruitment_data.xml',
|
||||
'data/website_hr_recruitment_data.xml',
|
||||
'views/website_hr_recruitment.xml',
|
||||
'security/ir.model.access.csv',
|
||||
'security/website_hr_recruitment_security.xml',
|
||||
],
|
||||
'demo': [
|
||||
'data/website_hr_recruitment_demo.xml',
|
||||
],
|
||||
'css': [
|
||||
'static/src/css/*.css'
|
||||
],
|
||||
|
||||
@@ -16,12 +16,8 @@
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<template id="index" name="Departments" page="True">
|
||||
<template id="index" name="Jobs" page="True">
|
||||
<t t-call="website.layout">
|
||||
<t t-set="head">
|
||||
<t t-raw="head or ''"/>
|
||||
</t>
|
||||
<t t-set="title">Jobs</t>
|
||||
<div id="wrap">
|
||||
<div class="oe_structure"/>
|
||||
<div class="container oe_website_jobs">
|
||||
@@ -64,7 +60,7 @@
|
||||
<script type="text/javascript" src="/website_hr_recruitment/static/src/js/recruitment.js"></script>
|
||||
<t t-raw="head or ''"/>
|
||||
</t>
|
||||
<t t-set="title">Job Detail</t>
|
||||
<t t-set="additional_title">Job Detail</t>
|
||||
<div id="wrap">
|
||||
<div class="container oe_website_hr_recruitment">
|
||||
<div class="row">
|
||||
@@ -110,7 +106,7 @@
|
||||
|
||||
<template id="applyjobpost">
|
||||
<t t-call="website.layout">
|
||||
<t t-set="title">Apply Job</t>
|
||||
<t t-set="additional_title">Apply Job</t>
|
||||
<div id="wrap">
|
||||
<div class="container">
|
||||
<h1 class="text-center">Apply for <span t-field="job.name"></span></h1>
|
||||
@@ -158,7 +154,7 @@
|
||||
</template>
|
||||
<template id="thankyou">
|
||||
<t t-call="website.layout">
|
||||
<t t-set="title">Thank You!</t>
|
||||
<t t-set="additional_title">Thank You!</t>
|
||||
<div id="wrap">
|
||||
<div class="container">
|
||||
<h1>You have successfully applied for job.</h1>
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
<!-- Page -->
|
||||
<template id="layout" name="Members Layout">
|
||||
<t t-call="website.layout">
|
||||
<t t-set="title">Associations</t>
|
||||
<t t-set="additional_title">Associations</t>
|
||||
<div id="wrap">
|
||||
<div class="oe_structure"/>
|
||||
<div class="container">
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
<!-- Task -->
|
||||
<template id="task" name="Task">
|
||||
<t t-call="website.layout">
|
||||
<t t-set="title">Task</t>
|
||||
<t t-set="additional_title">Task</t>
|
||||
<div id="wrap">
|
||||
<div class="container">
|
||||
<h4 t-field="task.name"/>
|
||||
@@ -41,7 +41,7 @@
|
||||
</template>
|
||||
<template id="index" name="Project">
|
||||
<t t-call="website.layout">
|
||||
<t t-set="title">Project</t>
|
||||
<t t-set="additional_title">Project</t>
|
||||
<div id="wrap">
|
||||
<div class="container">
|
||||
<h4 t-field="project.name"/>
|
||||
|
||||
@@ -13,6 +13,7 @@ OpenERP E-Commerce
|
||||
'data': [
|
||||
'website_sale_data.xml',
|
||||
'views/website_sale.xml',
|
||||
'views/website_sale_backend.xml',
|
||||
'security/ir.model.access.csv',
|
||||
'security/website_sale.xml',
|
||||
],
|
||||
|
||||
@@ -7,6 +7,8 @@ from openerp.addons.web.http import request
|
||||
from openerp.addons.website.models import website
|
||||
import random
|
||||
import uuid
|
||||
import urllib
|
||||
import simplejson
|
||||
|
||||
def get_order(order_id=None):
|
||||
order_obj = request.registry.get('sale.order')
|
||||
@@ -60,6 +62,11 @@ class Ecommerce(http.Controller):
|
||||
|
||||
_order = 'website_sequence desc, website_published desc'
|
||||
|
||||
def get_attribute_ids(self):
|
||||
attributes_obj = request.registry.get('product.attribute')
|
||||
attributes_ids = attributes_obj.search(request.cr, request.uid, [(1, "=", 1)], context=request.context)
|
||||
return attributes_obj.browse(request.cr, request.uid, attributes_ids, context=request.context)
|
||||
|
||||
def get_categories(self):
|
||||
domain = [('parent_id', '=', False)]
|
||||
|
||||
@@ -214,15 +221,63 @@ class Ecommerce(http.Controller):
|
||||
product_ids = [id for id in product_ids if id in product_obj.search(request.cr, request.uid, [("id", 'in', product_ids)], context=request.context)]
|
||||
return product_obj.browse(request.cr, request.uid, product_ids, context=request.context)
|
||||
|
||||
@website.route(['/shop/', '/shop/category/<cat_id>/', '/shop/category/<cat_id>/page/<int:page>/', '/shop/page/<int:page>/'], type='http', auth="public", multilang=True)
|
||||
def category(self, cat_id=0, page=0, **post):
|
||||
def has_search_attributes(self, attribute_id, value_id=None):
|
||||
if request.httprequest.args.get('attributes'):
|
||||
attributes = simplejson.loads(request.httprequest.args['attributes'])
|
||||
else:
|
||||
attributes = []
|
||||
for key_val in attributes:
|
||||
if key_val[0] == attribute_id and (not value_id or value_id in key_val[1:]):
|
||||
return key_val
|
||||
return False
|
||||
|
||||
@website.route(['/shop/attributes/'], type='http', auth="public", multilang=True)
|
||||
def attributes(self, **post):
|
||||
attributes = []
|
||||
index = []
|
||||
for key, val in post.items():
|
||||
cat = key.split("-")
|
||||
if len(cat) < 3 or cat[2] in ('max','minmem','maxmem'):
|
||||
continue
|
||||
cat_id = int(cat[1])
|
||||
if cat[2] == 'min':
|
||||
minmem = float(post.pop("att-%s-minmem" % cat[1]))
|
||||
maxmem = float(post.pop("att-%s-maxmem" % cat[1]))
|
||||
_max = int(post.pop("att-%s-max" % cat[1]))
|
||||
_min = int(val)
|
||||
if (minmem != _min or maxmem != _max) and cat_id not in index:
|
||||
attributes.append([cat_id , [_min, _max] ])
|
||||
index.append(cat_id)
|
||||
elif cat_id not in index:
|
||||
attributes.append([ cat_id, int(cat[2]) ])
|
||||
index.append(cat_id)
|
||||
else:
|
||||
attributes[index.index(cat_id)].append( int(cat[2]) )
|
||||
post.pop(key)
|
||||
|
||||
return request.redirect("/shop/?attributes=%s&%s" % (simplejson.dumps(attributes).replace(" ", ""), urllib.urlencode(post)))
|
||||
|
||||
def attributes_to_ids(self, attributes):
|
||||
obj = request.registry.get('product.attribute.product')
|
||||
domain = [(1, '=', 1)]
|
||||
for key_val in attributes:
|
||||
domain += [("attribute_id", "=", key_val[0])]
|
||||
if isinstance(key_val[1], list):
|
||||
domain += [("value", ">=", key_val[1][0]), ("value", "<=", key_val[1][1])]
|
||||
else:
|
||||
domain += [("value_id", "in", key_val[1:])]
|
||||
att_ids = obj.search(request.cr, request.uid, domain, context=request.context)
|
||||
att = obj.read(request.cr, request.uid, att_ids, ["product_id"], context=request.context)
|
||||
return [r["product_id"][0] for r in att]
|
||||
|
||||
@website.route(['/shop/', '/shop/page/<int:page>/'], type='http', auth="public", multilang=True)
|
||||
def category(self, category=0, attributes="", page=0, **post):
|
||||
|
||||
if 'promo' in post:
|
||||
self.change_pricelist(post.get('promo'))
|
||||
product_obj = request.registry.get('product.template')
|
||||
|
||||
domain = [("sale_ok", "=", True)]
|
||||
#domain += [('website_published', '=', True)]
|
||||
|
||||
if post.get("search"):
|
||||
domain += ['|', '|', '|',
|
||||
@@ -230,13 +285,20 @@ class Ecommerce(http.Controller):
|
||||
('description', 'ilike', "%%%s%%" % post.get("search")),
|
||||
('website_description', 'ilike', "%%%s%%" % post.get("search")),
|
||||
('product_variant_ids.public_categ_id.name', 'ilike', "%%%s%%" % post.get("search"))]
|
||||
if cat_id:
|
||||
cat_id = int(cat_id)
|
||||
domain += [('product_variant_ids.public_categ_id.id', 'child_of', cat_id)] + domain
|
||||
|
||||
if category:
|
||||
cat_id = int(category)
|
||||
domain = [('product_variant_ids.public_categ_id.id', 'child_of', cat_id)] + domain
|
||||
|
||||
if attributes:
|
||||
attributes = simplejson.loads(attributes)
|
||||
if attributes:
|
||||
ids = self.attributes_to_ids(attributes)
|
||||
domain = [('id', 'in', ids or [0] )] + domain
|
||||
|
||||
step = 20
|
||||
product_count = len(product_obj.search(request.cr, request.uid, domain, context=request.context))
|
||||
pager = request.website.pager(url="/shop/category/%s/" % cat_id, total=product_count, page=page, step=step, scope=7, url_args=post)
|
||||
pager = request.website.pager(url="/shop/%s" % post, total=product_count, page=page, step=step, scope=7, url_args=post)
|
||||
|
||||
request.context['pricelist'] = self.get_pricelist()
|
||||
|
||||
@@ -250,21 +312,18 @@ class Ecommerce(http.Controller):
|
||||
styles = style_obj.browse(request.cr, request.uid, style_ids, context=request.context)
|
||||
|
||||
values = {
|
||||
'get_categories': self.get_categories,
|
||||
'category_id': cat_id,
|
||||
'Ecommerce': self,
|
||||
'product_ids': product_ids,
|
||||
'product_ids_for_holes': fill_hole,
|
||||
'get_bin_packing_products': self.get_bin_packing_products,
|
||||
'get_products': self.get_products,
|
||||
'search': post.get("search"),
|
||||
'search': post or dict(),
|
||||
'pager': pager,
|
||||
'styles': styles,
|
||||
'style_in_product': lambda style, product: style.id in [s.id for s in product.website_style_ids]
|
||||
'style_in_product': lambda style, product: style.id in [s.id for s in product.website_style_ids],
|
||||
}
|
||||
return request.website.render("website_sale.products", values)
|
||||
|
||||
@website.route(['/shop/product/<int:product_id>/'], type='http', auth="public", multilang=True)
|
||||
def product(self, cat_id=0, product_id=0, **post):
|
||||
def product(self, product_id=0, **post):
|
||||
|
||||
if 'promo' in post:
|
||||
self.change_pricelist(post.get('promo'))
|
||||
@@ -284,12 +343,12 @@ class Ecommerce(http.Controller):
|
||||
product = product_obj.browse(request.cr, request.uid, product_id, context=request.context)
|
||||
|
||||
values = {
|
||||
'category_id': post.get('category_id') and int(post.get('category_id')) or None,
|
||||
'Ecommerce': self,
|
||||
'category': category,
|
||||
'search': post.get("search"),
|
||||
'get_categories': self.get_categories,
|
||||
'category_list': category_list,
|
||||
'main_object': product,
|
||||
'product': product,
|
||||
'search': post or dict(),
|
||||
}
|
||||
return request.website.render("website_sale.product", values)
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import website_styles
|
||||
import product
|
||||
import website_sale
|
||||
import product_attributes
|
||||
import website_sale
|
||||
import website
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
|
||||
from openerp.osv import osv, fields
|
||||
|
||||
|
||||
class attributes(osv.Model):
|
||||
_name = "product.attribute"
|
||||
|
||||
def _get_float_max(self, cr, uid, ids, field_name, arg, context=None):
|
||||
result = dict.fromkeys(ids, 0)
|
||||
if ids:
|
||||
cr.execute("""
|
||||
SELECT attribute_id, MAX(value)
|
||||
FROM product_attribute_product
|
||||
WHERE attribute_id in (%s)
|
||||
GROUP BY attribute_id
|
||||
""" % ",".join(map(str, ids)))
|
||||
result.update(dict(cr.fetchall()))
|
||||
return result
|
||||
|
||||
def _get_float_min(self, cr, uid, ids, field_name, arg, context=None):
|
||||
result = dict.fromkeys(ids, 0)
|
||||
if ids:
|
||||
cr.execute("""
|
||||
SELECT attribute_id, MIN(value)
|
||||
FROM product_attribute_product
|
||||
WHERE attribute_id in (%s)
|
||||
GROUP BY attribute_id
|
||||
""" % ",".join(map(str, ids)))
|
||||
result.update(dict(cr.fetchall()))
|
||||
return result
|
||||
|
||||
_columns = {
|
||||
'name': fields.char('Name', size=64, translate=True, required=True),
|
||||
'type': fields.selection([('distinct', 'Distinct'), ('float', 'Float')], "Type", required=True),
|
||||
'value_ids': fields.one2many('product.attribute.value', 'attribute_id', 'Values'),
|
||||
'product_ids': fields.one2many('product.attribute.product', 'attribute_id', 'Products'),
|
||||
|
||||
'float_max': fields.function(_get_float_max, type='float', string="Max", relation='product.attribute.product',store=True),
|
||||
'float_min': fields.function(_get_float_min, type='float', string="Min", relation='product.attribute.product',store=True),
|
||||
}
|
||||
_defaults = {
|
||||
'type': 'distinct'
|
||||
}
|
||||
|
||||
class attributes_value(osv.Model):
|
||||
_name = "product.attribute.value"
|
||||
_columns = {
|
||||
'name': fields.char('Value', size=64, translate=True, required=True),
|
||||
'attribute_id': fields.many2one('product.attribute', 'Attribute', required=True),
|
||||
'product_ids': fields.one2many('product.attribute.product', 'value_id', 'Products'),
|
||||
}
|
||||
|
||||
class attributes_product(osv.Model):
|
||||
_name = "product.attribute.product"
|
||||
_columns = {
|
||||
'value': fields.float('Value'),
|
||||
'value_id': fields.many2one('product.attribute.value', 'Distinct Value'),
|
||||
'attribute_id': fields.many2one('product.attribute', 'Attribute', required=True),
|
||||
'product_id': fields.many2one('product.template', 'Product', required=True),
|
||||
|
||||
'type': fields.related('attribute_id', 'type', type='selection',
|
||||
selection=[('distinct', 'Distinct'), ('float', 'Float')], string='Type'),
|
||||
}
|
||||
|
||||
def onchange_attribute_id(self, cr, uid, ids, attribute_id, context=None):
|
||||
attribute = self.pool.get('product.attribute').browse(cr, uid, attribute_id, context=context)
|
||||
return {'value': {'type': attribute.type, 'value_id': False, 'value': ''}}
|
||||
|
||||
class product_template(osv.Model):
|
||||
_inherit = "product.template"
|
||||
_columns = {
|
||||
'website_attribute_ids': fields.one2many('product.attribute.product', 'product_id', 'Product Attributes'),
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from openerp.osv import orm, fields
|
||||
from openerp.addons.web import http
|
||||
|
||||
class Website(orm.Model):
|
||||
_inherit = 'website'
|
||||
|
||||
def _get_pricelist(self, cr, uid, ids, field_name, arg, context=None):
|
||||
# FIXME: oh god kill me now
|
||||
pricelist_id = http.request.httprequest.session['ecommerce_pricelist']
|
||||
return dict.fromkeys(
|
||||
ids, self.pool['product.pricelist'].browse(
|
||||
cr, uid, pricelist_id, context=context))
|
||||
|
||||
_columns = {
|
||||
'pricelist_id': fields.function(
|
||||
_get_pricelist, type='many2one', obj='product.pricelist')
|
||||
}
|
||||
@@ -119,4 +119,36 @@ $(document).ready(function () {
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
$(".js_slider").each(function() {
|
||||
var $slide = $(this);
|
||||
var $slider = $('<div>'+
|
||||
'<input type="hidden" name="att-'+$slide.data("id")+'-minmem" value="'+$slide.data("min")+'"/>'+
|
||||
'<input type="hidden" name="att-'+$slide.data("id")+'-maxmem" value="'+$slide.data("max")+'"/>'+
|
||||
'</div>');
|
||||
var $min = $("<input readonly name='att-"+$slide.data("id")+"-min'/>")
|
||||
.css("border", "0").css("width", "50%")
|
||||
.val($slide.data("min"));
|
||||
var $max = $("<input readonly name='att-"+$slide.data("id")+"-max'/>")
|
||||
.css("border", "0").css("width", "50%").css("text-align", "right")
|
||||
.val($slide.data("max"));
|
||||
$slide.append($min);
|
||||
$slide.append($max);
|
||||
$slide.append($slider);
|
||||
$slider.slider({
|
||||
range: true,
|
||||
min: +$slide.data("min"),
|
||||
max: +$slide.data("max"),
|
||||
values: [
|
||||
$slide.data("value-min") ? +$slide.data("value-min") : +$slide.data("min"),
|
||||
$slide.data("value-max") ? +$slide.data("value-max") : +$slide.data("max")
|
||||
],
|
||||
slide: function( event, ui ) {
|
||||
$min.val( ui.values[ 0 ] );
|
||||
$max.val( ui.values[ 1 ] );
|
||||
}
|
||||
});
|
||||
$min.val( $slider.slider( "values", 0 ) );
|
||||
$max.val( $slider.slider( "values", 1 ) );
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2,75 +2,6 @@
|
||||
<openerp>
|
||||
<data>
|
||||
|
||||
<record id="product_normal_form_view" model="ir.ui.view">
|
||||
<field name="name">product.normal.form.inherit</field>
|
||||
<field name="model">product.product</field>
|
||||
<field name="inherit_id" ref="product.product_normal_form_view"/>
|
||||
<field name="arch" type="xml">
|
||||
<!-- add state field in header -->
|
||||
<xpath expr="//sheet" position="before">
|
||||
<div class="oe_form_box_info oe_text_center" attrs="{'invisible': [('sale_ok', '=', False)]}">
|
||||
<p attrs="{'invisible': [('website_published', '=', True)]}">
|
||||
This product is <b>not available</b> for public user in your ecommerce.
|
||||
</p>
|
||||
<p attrs="{'invisible': [('website_published', '=', False)]}">
|
||||
This product is <b>available</b> for public user in your ecommerce.
|
||||
</p>
|
||||
<p>Website view: <field class="oe_inline" name="website_url" widget="url"/></p>
|
||||
</div>
|
||||
</xpath>
|
||||
|
||||
<group name="sale" position="inside">
|
||||
<group name="website" string="Website">
|
||||
<field name="website_published"/>
|
||||
<field name="suggested_product_ids" widget="many2many_tags"/>
|
||||
<field name="website_style_ids" widget="many2many_tags"/>
|
||||
</group>
|
||||
</group>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="product_pricelist_view">
|
||||
<field name="name">product.pricelist.website.form</field>
|
||||
<field name="model">product.pricelist</field>
|
||||
<field name="inherit_id" ref="product.product_pricelist_view"/>
|
||||
<field name="arch" type="xml">
|
||||
<field name="active" position="after">
|
||||
<field name="code"/>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="product_template_form_view">
|
||||
<field name="name">product.template.product.website.form</field>
|
||||
<field name="model">product.template</field>
|
||||
<field name="inherit_id" ref="product.product_template_form_view"/>
|
||||
<field name="arch" type="xml">
|
||||
<!-- add state field in header -->
|
||||
<xpath expr="//sheet" position="before">
|
||||
<div class="oe_form_box_info oe_text_center" attrs="{'invisible': [('sale_ok', '=', False)]}">
|
||||
<p attrs="{'invisible': [('website_published', '=', True)]}">
|
||||
This product is <b>not available</b> for public user in your ecommerce.
|
||||
</p>
|
||||
<p attrs="{'invisible': [('website_published', '=', False)]}">
|
||||
This product is <b>available</b> for public user in your ecommerce.
|
||||
</p>
|
||||
<p>Website view: <field class="oe_inline" name="website_url" widget="url"/></p>
|
||||
</div>
|
||||
</xpath>
|
||||
|
||||
<xpath expr="//group[@string='Product Type']" position="inside">
|
||||
<field name="website_published"/>
|
||||
</xpath>
|
||||
<xpath expr="//page[@string='Information']" position="inside">
|
||||
<group colspan="4" string="Products On Ecommerce">
|
||||
<field name="suggested_product_ids" widget="many2many_tags"/>
|
||||
<field name="website_style_ids" widget="many2many_tags"/>
|
||||
</group>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- Layout add nav and footer -->
|
||||
|
||||
<template id="header" inherit_id="website.layout" name="Custom Header">
|
||||
@@ -85,11 +16,12 @@
|
||||
</li>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<!-- List of categories -->
|
||||
|
||||
<template id="categories_recursive">
|
||||
<li t-att-class="category.id == category_id and 'active' or ''">
|
||||
<a t-att-class="category.id not in categ[1] and 'unpublish' or ''" t-href="/shop/category/#{ category.id }/" t-field="category.name"></a>
|
||||
<li t-att-class="category.id == search.get('category') and 'active' or ''">
|
||||
<a t-att-class="category.id not in categ[1] and 'unpublish' or ''" t-href="/shop/?category=#{ category.id }" t-field="category.name" t-keep-query="search,facettes"></a>
|
||||
<ul t-if="category.child_id" class="nav nav-pills nav-stacked nav-hierarchy">
|
||||
<t t-foreach="category.child_id" t-as="category">
|
||||
<t t-if="category.id in categ[1] or editable">
|
||||
@@ -107,22 +39,27 @@
|
||||
<div class="ribbon">Promo</div>
|
||||
</div>
|
||||
<div class="oe_product_description">
|
||||
<a t-href="/shop/product/#{ product.id }/?#{ search and ('search=%s' % search) or ''}#{ category_id and ('&category_id=%s' % category_id) or ''}">
|
||||
<a t-href="/shop/product/#{ product.id }/" t-keep-query="category,search,facettes">
|
||||
<b t-field="product.name"/>
|
||||
</a>
|
||||
</div>
|
||||
<div class="product_price">
|
||||
<b>
|
||||
<t t-if="product.product_variant_ids[0].lst_price != product.product_variant_ids[0].price">
|
||||
<span class="text-danger" style="text-decoration: line-through;">
|
||||
<t t-esc="product.product_variant_ids[0].lst_price" /> €
|
||||
</span>&nbsp;
|
||||
<del class="text-danger"
|
||||
t-field="product.product_variant_ids[0].lst_price" t-field-options='{
|
||||
"widget": "monetary",
|
||||
"display_currency": "website.pricelist_id.currency_id"
|
||||
}'/>&nbsp;
|
||||
</t>
|
||||
<t t-esc="product.product_variant_ids[0].price" /> €
|
||||
<span t-field="product.product_variant_ids[0].price" t-field-options='{
|
||||
"widget": "monetary",
|
||||
"display_currency": "website.pricelist_id.currency_id"
|
||||
}'/>
|
||||
</b>
|
||||
</div>
|
||||
<div class="oe_product_image text-center">
|
||||
<a t-href="/shop/product/#{ product.id }/?#{ search and ('search=%s' % search) or ''}#{ category_id and ('&category_id=%s' % category_id) or ''}">
|
||||
<a t-href="/shop/product/#{ product.id }/" t-keep-query="category,search,facettes">
|
||||
<img class="img" t-att-src="product.img('image')"/>
|
||||
</a>
|
||||
</div>
|
||||
@@ -135,7 +72,7 @@
|
||||
<link rel='stylesheet' href='/website_sale/static/src/css/website_sale.css'/>
|
||||
<t t-raw="head or ''"/>
|
||||
</t>
|
||||
<t t-set="title">Our Products</t>
|
||||
<t t-set="additional_title">Our Products</t>
|
||||
<div id="wrap">
|
||||
<div class="oe_structure"/>
|
||||
<div class="container oe_website_sale">
|
||||
@@ -143,14 +80,14 @@
|
||||
<div class="col-sm-4">
|
||||
<h1>Our Products</h1>
|
||||
</div><div class="col-sm-2 pagination text-center">
|
||||
<a t-if="editable" t-href="/shop/#{ category_id and ('category/%s/' % category_id) or ''}add_product/" class="btn btn-primary btn-default">New Product</a>
|
||||
<a t-if="editable" t-href="/shop/add_product/" class="btn btn-primary btn-default" t-keep-query="category,search,facettes">New Product</a>
|
||||
</div><div class="col-sm-6">
|
||||
<t t-call="website.pager">
|
||||
<t t-set="classname">pull-right</t>
|
||||
</t>
|
||||
<form t-action="/shop/#{ category_id and ('category/%s/' % category_id) or ''}" method="get" class="pull-right pagination form-inline" style="padding-right: 5px;">
|
||||
<form t-action="/shop/" method="get" class="pull-right pagination form-inline" style="padding-right: 5px;" t-keep-query="category,search,facettes">
|
||||
<div class="form-group">
|
||||
<input type="text" name="search" class="search-query form-control" placeholder="Search..." t-att-value="search or ''"/>
|
||||
<input type="text" name="search" class="search-query form-control" placeholder="Search..." t-att-value="search.get('search') or ''"/>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
@@ -160,12 +97,13 @@
|
||||
<div class="col-md-12" id="products_grid">
|
||||
<table width="100%">
|
||||
<tbody>
|
||||
<t t-set="table_products" t-value="get_bin_packing_products(product_ids, product_ids_for_holes, 4)"/>
|
||||
<t t-set="table_products" t-value="Ecommerce.get_bin_packing_products(product_ids, product_ids_for_holes, 4)"/>
|
||||
<tr t-foreach="table_products" t-as="tr_product">
|
||||
<t t-foreach="tr_product" t-as="td_product">
|
||||
<t t-if="td_product">
|
||||
<t t-set="product" t-value="td_product['product']"/>
|
||||
<td t-att-colspan="td_product['x']"
|
||||
t-attf-width="#{td_product['x']*25}%"
|
||||
t-att-rowspan="td_product['y']"
|
||||
t-attf-class="oe_product oe-height-#{td_product['y']*2} #{ td_product['class'] }">
|
||||
|
||||
@@ -266,7 +204,7 @@
|
||||
<template id="list_view" inherit_option_id="website_sale.products" name="List View">
|
||||
<xpath expr="//div[@id='products_grid']/table" position="replace">
|
||||
<div class="row">
|
||||
<t t-set="products" t-value="get_products(product_ids)"/>
|
||||
<t t-set="products" t-value="Ecommerce.get_products(product_ids)"/>
|
||||
<t t-foreach="products" t-as="product">
|
||||
<div class="col-md-12 oe_list_products oe-height-1">
|
||||
<t t-call="website_sale.products_cart"/>
|
||||
@@ -284,7 +222,7 @@
|
||||
<t t-set="head">
|
||||
<script type="text/javascript" src="/website_sale/static/src/js/website_sale.js"></script>
|
||||
</t>
|
||||
<t t-set="title" t-raw="product.name"/>
|
||||
<t t-set="additional_title" t-value="product.name"/>
|
||||
<div id="wrap">
|
||||
|
||||
<section class="container mt8">
|
||||
@@ -292,7 +230,7 @@
|
||||
<div class="col-sm-5">
|
||||
<ol class="breadcrumb">
|
||||
<li><a t-href="/shop">Products</a></li>
|
||||
<li t-if="category"><a t-att-href="'/shop/category/%s' % (category_id,)"><span t-field="category.name"/></a></li>
|
||||
<li t-if="search.get('category')"><a t-att-href="'/shop/" t-keep-query="category,search,facettes"><span t-field="category.name"/></a></li>
|
||||
<li class="active"><span t-field="product.name"></span></li>
|
||||
</ol>
|
||||
</div><div class="col-sm-3">
|
||||
@@ -308,8 +246,13 @@
|
||||
</li>
|
||||
</t>
|
||||
</div><div class="col-sm-3 col-sm-offset-1">
|
||||
<form t-action="/shop/#{ category_id and ('category/%s/' % category_id) or ''}" method="get" class="pull-right">
|
||||
<form t-action="/shop/" method="get" class="pull-right" t-keep-query="category,facettes">
|
||||
<div class="input-group">
|
||||
<t t-if="search">
|
||||
<t foreach="search.items()" t-as="key">
|
||||
<input t-att-name="key[0]" t-att-value="key[1]"/>
|
||||
</t>
|
||||
</t>
|
||||
<span class="input-group-addon"><span class="glyphicon glyphicon-search"/></span>
|
||||
<input type="text" name="search" class="search-query form-control" placeholder="Search..." t-att-value="search or ''"/>
|
||||
</div>
|
||||
@@ -321,7 +264,7 @@
|
||||
<section class="container oe_website_sale mb16" id="product_detail">
|
||||
<div class="row">
|
||||
<div class="col-sm-7 col-md-7 col-lg-7">
|
||||
<img class="img" t-att-src="product.img('image')" style="max-height: 500px"/>
|
||||
<span t-field="product.image" style="max-height: 500px" t-field-options='{"widget": "image"}'/>
|
||||
</div><div class="col-sm-5 col-md-5 col-lg-4 col-lg-offset-1">
|
||||
<h1 t-field="product.name">Product Name</h1>
|
||||
|
||||
@@ -332,7 +275,10 @@
|
||||
<input type="radio" name="product_id" t-att-value="product.id" t-att-checked="product == product.product_variant_ids[0] or None"/>
|
||||
<t t-esc="product.variants or ''">Standard</t>
|
||||
<span class="badge" t-if="product.price_extra">
|
||||
<t t-esc="product.price_extra > 0 and '+' or ''"/><span t-esc="product.price_extra"/> €
|
||||
<t t-esc="product.price_extra > 0 and '+' or ''"/><span t-field="product.price_extra" t-field-options='{
|
||||
"widget": "monetary",
|
||||
"display_currency": "website.pricelist_id.currency_id"
|
||||
}'/>
|
||||
</span>
|
||||
</label>
|
||||
<br/>
|
||||
@@ -343,10 +289,11 @@
|
||||
<b><span class="oe_price" t-esc="product.product_variant_ids[0].price" /> €</b>
|
||||
|
||||
<t t-if="product.product_variant_ids[0].lst_price != product.product_variant_ids[0].price">
|
||||
&nbsp;&nbsp;<small class="text-danger" style="text-decoration: line-through;">
|
||||
<span class="text-danger" style="text-decoration: line-through;">
|
||||
<t t-esc="product.product_variant_ids[0].lst_price" /> €
|
||||
</small>
|
||||
</span>&nbsp;
|
||||
</t>
|
||||
<h4><b><span class="oe_price" t-esc="product.product_variant_ids[0].price" /> €</b></h4>
|
||||
</h4>
|
||||
</div>
|
||||
<button class="btn btn-primary btn-lg mt8">Add to Cart</button>
|
||||
@@ -375,7 +322,7 @@
|
||||
<t t-foreach="product.recommended_products()" t-as="product">
|
||||
<div class='col-md-2 thumbnail' style='width: 170px; margin-right: 16px;'>
|
||||
<div class='mt16 text-center'>
|
||||
<img t-att-src="product.img('image_small')"/>
|
||||
<span t-field="product.image_small"/>
|
||||
<h5>
|
||||
<a t-href="/shop/product/#{ product.id }/"
|
||||
style="display: block">
|
||||
@@ -393,14 +340,13 @@
|
||||
|
||||
<!-- Page Shop my cart -->
|
||||
|
||||
<template id="mycart" name="My cart" page="True">
|
||||
<template id="mycart" name="Your Cart" page="True">
|
||||
<t t-call="website.layout">
|
||||
<t t-set="head">
|
||||
<script type="text/javascript" src="/website_sale/static/src/js/website_sale.js"></script>
|
||||
<link rel='stylesheet' href='/website_sale/static/src/css/website_sale.css'/>
|
||||
<t t-raw="head or ''"/>
|
||||
</t>
|
||||
<t t-set="title">Your Cart</t>
|
||||
<div id="wrap">
|
||||
<div class="container oe_website_sale">
|
||||
|
||||
@@ -435,7 +381,8 @@
|
||||
<tr t-foreach="website_sale_order.order_line" t-as="line">
|
||||
<td colspan="2" t-if="not line.product_id.product_tmpl_id"></td>
|
||||
<td t-if="line.product_id.product_tmpl_id">
|
||||
<img class="img-rounded" t-att-src="line.product_id.img('image_small')"/>
|
||||
<span t-field="line.product_id.image_small"
|
||||
t-field-options='{"widget": "image", "class": "img-rounded"}'/>
|
||||
</td>
|
||||
<td t-if="line.product_id.product_tmpl_id">
|
||||
<div>
|
||||
@@ -446,10 +393,16 @@
|
||||
<div class="text-muted" t-field="line.product_id.description_sale"/>
|
||||
</td>
|
||||
<td class="text-center">
|
||||
<div class="text-danger" style="text-decoration: line-through;" t-if="line.product_id.lst_price > line.price_unit">
|
||||
<t t-esc="line.product_id.lst_price" /> €
|
||||
</div>
|
||||
<span t-field="line.price_unit"></span> €
|
||||
<del class="text-danger" t-if="line.product_id.lst_price > line.price_unit">
|
||||
<span t-field="line.product_id.lst_price" t-field-options='{
|
||||
"widget": "monetary",
|
||||
"display_currency": "website.pricelist_id.currency_id"
|
||||
}'/>
|
||||
</del>
|
||||
<span t-field="line.price_unit" t-field-options='{
|
||||
"widget": "monetary",
|
||||
"display_currency": "website.pricelist_id.currency_id"
|
||||
}'/>
|
||||
</td>
|
||||
<td>
|
||||
<div class="input-group">
|
||||
@@ -479,11 +432,21 @@
|
||||
<thead>
|
||||
<tr style="border-top: 1px solid #000">
|
||||
<th><h3>Total:</h3></th>
|
||||
<th class="text-right"><h3><t t-esc="website_sale_order and website_sale_order.amount_total or 0"/> €</h3></th>
|
||||
<th class="text-right"><h3>
|
||||
<span t-field="website_sale_order.amount_total" t-field-options='{
|
||||
"widget": "monetary",
|
||||
"display_currency": "website.pricelist_id.currency_id"
|
||||
}'/></h3>
|
||||
</th>
|
||||
</tr>
|
||||
<tr class="text-muted">
|
||||
<td><abbr title="Taxes may be updated after providing shipping address">Incl. Taxes:</abbr></td>
|
||||
<td class="text-right"><t t-esc="website_sale_order and website_sale_order.amount_tax or 0"/> €</td>
|
||||
<td class="text-right">
|
||||
<span t-field="website_sale_order.amount_tax" t-field-options='{
|
||||
"widget": "monetary",
|
||||
"display_currency": "website.pricelist_id.currency_id"
|
||||
}'/>
|
||||
</td>
|
||||
</tr>
|
||||
</thead>
|
||||
</table>
|
||||
@@ -519,10 +482,10 @@
|
||||
|
||||
<template id="products_categories" inherit_option_id="website_sale.products" name="Product Categories">
|
||||
<xpath expr="//div[@id='products_grid']" position="before">
|
||||
<div class="col-md-3">
|
||||
<div id="categories" class="col-md-3">
|
||||
<ul class="nav nav-pills nav-stacked mt16">
|
||||
<li t-att-class=" '' if category_id else 'active' "><a t-href="/shop/">All Products</a></li>
|
||||
<t t-set="categ" t-value="get_categories()"/>
|
||||
<li t-att-class=" '' if search.get('category') else 'active' "><a t-href="/shop/">All Products</a></li>
|
||||
<t t-set="categ" t-value="Ecommerce.get_categories()"/>
|
||||
<t t-foreach="categ[0]" t-as="category">
|
||||
<t t-call="website_sale.categories_recursive"/>
|
||||
</t>
|
||||
@@ -534,6 +497,42 @@
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<template id="products_attributes" inherit_option_id="website_sale.products_categories" name="Product Attributes">
|
||||
<xpath expr="//div[@id='categories']" position="inside">
|
||||
<form t-action="/shop/attributes/" method="post" t-keep-query="category,search">
|
||||
<ul class="nav nav-pills nav-stacked mt16">
|
||||
<t t-set="attribute_ids" t-value="Ecommerce.get_attribute_ids()"/>
|
||||
<t t-foreach="attribute_ids" t-as="attribute_id">
|
||||
<li t-if="attribute_id.value_ids and attribute_id.type == 'distinct'">
|
||||
<div t-field="attribute_id.name"/>
|
||||
<ul class="nav nav-pills nav-stacked">
|
||||
<t t-foreach="attribute_id.value_ids" t-as="value_id">
|
||||
<li t-att-class="Ecommerce.has_search_attributes(attribute_id.id, value_id.id) and 'active' or ''">
|
||||
<label style="margin: 0 20px;">
|
||||
<input type="checkbox" t-att-name="'att-%s-%s' % (attribute_id.id, value_id.id)"
|
||||
t-att-checked="Ecommerce.has_search_attributes(attribute_id.id, value_id.id) and 'checked' or ''"/>
|
||||
<span style="font-weight: normal" t-field="value_id.name"/>
|
||||
</label>
|
||||
</li>
|
||||
</t>
|
||||
</ul>
|
||||
</li>
|
||||
<li t-if="attribute_id.type == 'float' and attribute_id.float_min != attribute_id.float_max">
|
||||
<div t-field="attribute_id.name"/>
|
||||
<t t-set="attribute" t-value="Ecommerce.has_search_attributes(attribute_id.id)"/>
|
||||
<div style="margin: 0 20px;" class="js_slider"
|
||||
t-att-data-id="attribute_id.id"
|
||||
t-att-data-value-min="attribute and attribute[1][0] or attribute_id.float_min"
|
||||
t-att-data-value-max="attribute and attribute[1][1] or attribute_id.float_max"
|
||||
t-att-data-min="attribute_id.float_min"
|
||||
t-att-data-max="attribute_id.float_max"></div>
|
||||
</li>
|
||||
</t>
|
||||
</ul>
|
||||
<button class="btn btn-xs btn-primary mt16">Apply filter</button>
|
||||
</form>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<template id="suggested_products_list" inherit_id="website_sale.mycart" inherit_option_id="website_sale.mycart" name="Suggested Products in list view">
|
||||
<xpath expr="//table[@id='mycart_products']" position="after">
|
||||
@@ -554,7 +553,8 @@
|
||||
|
||||
<td>
|
||||
<a t-href="/shop/product/#{ product.product_tmpl_id.id }/">
|
||||
<img class="img-rounded" t-att-src="product.img('image_small')"/>
|
||||
<span t-field="product.image_small"
|
||||
t-field-options='{"widget": "image", "class": "img-rounded"}'/>
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
@@ -566,7 +566,10 @@
|
||||
<div class="text-muted" t-field="product.description_sale"/>
|
||||
</td>
|
||||
<td>
|
||||
<span t-field="product.lst_price"></span> €
|
||||
<span t-field="product.lst_price" t-field-options='{
|
||||
"widget": "monetary",
|
||||
"display_currency": "website.pricelist_id.currency_id"
|
||||
}'/>
|
||||
</td>
|
||||
<td class="text-center">
|
||||
<a t-href="./add_cart/?product_id=#{ product.id }"><strong>Add to Cart</strong></a>
|
||||
@@ -604,7 +607,7 @@
|
||||
<link rel='stylesheet' href='/website_sale/static/src/css/website_sale.css'/>
|
||||
<t t-raw="head or ''"/>
|
||||
</t>
|
||||
<t t-set="title">Shop - Checkout</t>
|
||||
<t t-set="additional_title">Shop - Checkout</t>
|
||||
<div id="wrap">
|
||||
<div class="container oe_website_sale">
|
||||
<ul class="wizard pull-right">
|
||||
@@ -734,11 +737,20 @@
|
||||
<h3 class="page-header mt16">Your Order <small><a href="/shop/mycart"><span class="icon-arrow-right"/> change</a></small></h3>
|
||||
<div class="row">
|
||||
<div class="col-sm-6 text-right">Subtotal:</div>
|
||||
<div class="col-sm-6"><t t-esc="website_sale_order.amount_untaxed or 0"/> €</div>
|
||||
<div class="col-sm-6"><span t-esc="website_sale_order.amount_untaxed" t-field-options='{
|
||||
"widget": "monetary",
|
||||
"display_currency": "website.pricelist_id.currency_id"
|
||||
}'/></div>
|
||||
<div class="col-sm-6 text-right">Taxes:</div>
|
||||
<div class="col-sm-6"><t t-esc="website_sale_order.amount_tax or 0"/> €</div>
|
||||
<div class="col-sm-6"><span t-field="website_sale_order.amount_tax" t-field-options='{
|
||||
"widget": "monetary",
|
||||
"display_currency": "website.pricelist_id.currency_id"
|
||||
}'/></div>
|
||||
<div class="col-sm-6 text-right"><h4>Total To Pay:</h4></div>
|
||||
<div class="col-sm-6"><h4><t t-esc="website_sale_order.amount_total or 0"/> €</h4></div>
|
||||
<div class="col-sm-6"><h4><span t-field="website_sale_order.amount_total" t-field-options='{
|
||||
"widget": "monetary",
|
||||
"display_currency": "website.pricelist_id.currency_id"
|
||||
}'/></h4></div>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-default btn-primary pull-right mt16" t-if="user_id.id != website.public_user.id">
|
||||
Confirm <span class="icon-long-arrow-right"/>
|
||||
@@ -759,7 +771,7 @@
|
||||
<link rel='stylesheet' href='/website_sale/static/src/css/website_sale.css'/>
|
||||
<t t-raw="head or ''"/>
|
||||
</t>
|
||||
<t t-set="title">Select Payment Mode</t>
|
||||
<t t-set="additional_title">Shop - Select Payment Mode</t>
|
||||
<div id="wrap">
|
||||
<div class="container oe_website_sale">
|
||||
|
||||
@@ -790,13 +802,19 @@
|
||||
<tr t-foreach="website_sale_order.order_line" t-as="line">
|
||||
<td colspan="2" t-if="not line.product_id.product_tmpl_id"></td>
|
||||
<td t-if="line.product_id.product_tmpl_id">
|
||||
<a t-href="/shop/product/#{ line.product_id.product_tmpl_id.id }/"><img class="img-rounded" t-att-src="line.product_id.img('image_small')"/></a>
|
||||
<a t-href="/shop/product/#{ line.product_id.product_tmpl_id.id }/">
|
||||
<span t-field="line.product_id.image_small"
|
||||
t-field-options='{"widget": "image", "class": "img-rounded"}'/>
|
||||
</a>
|
||||
</td>
|
||||
<td t-if="line.product_id.product_tmpl_id">
|
||||
<strong t-field="line.product_id.name"/>
|
||||
</td>
|
||||
<td class="text-center">
|
||||
<span t-field="line.price_unit"></span> €
|
||||
<span t-field="line.price_unit" t-field-options='{
|
||||
"widget": "monetary",
|
||||
"display_currency": "website.pricelist_id.currency_id"
|
||||
}'/>
|
||||
</td>
|
||||
<td>
|
||||
<div t-esc="line.product_uom_qty"/>
|
||||
@@ -812,11 +830,17 @@
|
||||
<thead>
|
||||
<tr style="border-top: 1px solid #000">
|
||||
<th><h3>Total:</h3></th>
|
||||
<th class="text-right"><h3><t t-esc="website_sale_order and website_sale_order.amount_total or 0"/> €</h3></th>
|
||||
<th class="text-right"><h3><span t-field="website_sale_order.amount_total" t-field-options='{
|
||||
"widget": "monetary",
|
||||
"display_currency": "website.pricelist_id.currency_id"
|
||||
}'/></h3></th>
|
||||
</tr>
|
||||
<tr class="text-muted">
|
||||
<td><abbr title="Taxes may be updated after providing shipping address">Incl. Taxes:</abbr></td>
|
||||
<td class="text-right"><t t-esc="website_sale_order and website_sale_order.amount_tax or 0"/> €</td>
|
||||
<td class="text-right"><span t-field="website_sale_order.amount_tax" t-field-options='{
|
||||
"widget": "monetary",
|
||||
"display_currency": "website.pricelist_id.currency_id"
|
||||
}'/></td>
|
||||
</tr>
|
||||
</thead>
|
||||
</table>
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<data>
|
||||
|
||||
|
||||
<record id="product_normal_form_view" model="ir.ui.view">
|
||||
<field name="name">product.normal.form.inherit</field>
|
||||
<field name="model">product.product</field>
|
||||
<field name="inherit_id" ref="product.product_normal_form_view"/>
|
||||
<field name="arch" type="xml">
|
||||
<!-- add state field in header -->
|
||||
<xpath expr="//sheet" position="before">
|
||||
<div class="oe_form_box_info oe_text_center" attrs="{'invisible': [('sale_ok', '=', False)]}">
|
||||
<p attrs="{'invisible': [('website_published', '=', True)]}">
|
||||
This product is <b>not available</b> for public user in your ecommerce.
|
||||
</p>
|
||||
<p attrs="{'invisible': [('website_published', '=', False)]}">
|
||||
This product is <b>available</b> for public user in your ecommerce.
|
||||
</p>
|
||||
<p>Website view: <field class="oe_inline" name="website_url" widget="url"/></p>
|
||||
</div>
|
||||
</xpath>
|
||||
|
||||
<group name="sale" position="inside">
|
||||
<group name="website" string="Website">
|
||||
<field name="website_published"/>
|
||||
<field name="suggested_product_ids" widget="many2many_tags"/>
|
||||
<field name="website_style_ids" widget="many2many_tags"/>
|
||||
</group>
|
||||
</group>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="product_pricelist_view">
|
||||
<field name="name">product.pricelist.website.form</field>
|
||||
<field name="model">product.pricelist</field>
|
||||
<field name="inherit_id" ref="product.product_pricelist_view"/>
|
||||
<field name="arch" type="xml">
|
||||
<field name="active" position="after">
|
||||
<field name="code"/>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="product_template_form_view">
|
||||
<field name="name">product.template.product.website.form</field>
|
||||
<field name="model">product.template</field>
|
||||
<field name="inherit_id" ref="product.product_template_form_view"/>
|
||||
<field name="arch" type="xml">
|
||||
<!-- add state field in header -->
|
||||
<xpath expr="//sheet" position="before">
|
||||
<div class="oe_form_box_info oe_text_center" attrs="{'invisible': [('sale_ok', '=', False)]}">
|
||||
<p attrs="{'invisible': [('website_published', '=', True)]}">
|
||||
This product is <b>not available</b> for public user in your ecommerce.
|
||||
</p>
|
||||
<p attrs="{'invisible': [('website_published', '=', False)]}">
|
||||
This product is <b>available</b> for public user in your ecommerce.
|
||||
</p>
|
||||
<p>Website view: <field class="oe_inline" name="website_url" widget="url"/></p>
|
||||
</div>
|
||||
</xpath>
|
||||
|
||||
<xpath expr="//group[@string='Product Type']" position="inside">
|
||||
<field name="website_published"/>
|
||||
</xpath>
|
||||
<xpath expr="//page[@string='Information']" position="inside">
|
||||
<group colspan="4" string="Products On Ecommerce">
|
||||
<field name="suggested_product_ids" widget="many2many_tags"/>
|
||||
<field name="website_style_ids" widget="many2many_tags"/>
|
||||
<field colspan="4" name="website_attribute_ids" nolabel="1">
|
||||
<tree string="Product Attributes" editable="bottom">
|
||||
<field name="attribute_id" on_change="onchange_attribute_id(attribute_id)"/>
|
||||
<field name="type" invisible="1"/>
|
||||
<field name="value" attrs="{'required': [('type','=','float')]}"/>
|
||||
<field name="value_id"
|
||||
attrs="{'required': [('type','=','distinct')]}"
|
||||
context="{'default_attribute_id': attribute_id}"
|
||||
domain="[('attribute_id', '=', attribute_id)]"/>
|
||||
</tree>
|
||||
</field>
|
||||
</group>
|
||||
</xpath>
|
||||
|
||||
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
Reference in New Issue
Block a user