[MERGE] forward port branch saas-11.2 up to b170a753e1
This commit is contained in:
@@ -35,13 +35,14 @@
|
||||
# Yuriy Ney <k_wizard@mail.ru>, 2017
|
||||
# Yuriy Ney <kwizardn@gmail.com>, 2018
|
||||
# sergeiruzkiicode <sergei.ruzki@icode.by>, 2018
|
||||
# Илья Пономарев <horochiy92@gmail.com>, 2018
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Odoo Server saas~11.2\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2018-03-22 14:10+0000\n"
|
||||
"PO-Revision-Date: 2018-03-22 14:10+0000\n"
|
||||
"Last-Translator: sergeiruzkiicode <sergei.ruzki@icode.by>, 2018\n"
|
||||
"Last-Translator: Илья Пономарев <horochiy92@gmail.com>, 2018\n"
|
||||
"Language-Team: Russian (https://www.transifex.com/odoo/teams/41243/ru/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@@ -4790,12 +4791,12 @@ msgstr ""
|
||||
#. module: account
|
||||
#: model:ir.ui.view,arch_db:account.res_config_settings_view_form
|
||||
msgid "Import your bank statements in CAMT.053"
|
||||
msgstr ""
|
||||
msgstr "Импорт банковской выписки в формате CAMT.053"
|
||||
|
||||
#. module: account
|
||||
#: model:ir.ui.view,arch_db:account.res_config_settings_view_form
|
||||
msgid "Import your bank statements in CSV"
|
||||
msgstr ""
|
||||
msgstr "Импорт Банковской Выписки в формате CSV"
|
||||
|
||||
#. module: account
|
||||
#: model:ir.ui.view,arch_db:account.res_config_settings_view_form
|
||||
|
||||
@@ -396,7 +396,7 @@ class AccountInvoice(models.Model):
|
||||
""" computes the prefix of the number that will be assigned to the first invoice/bill/refund of a journal, in order to
|
||||
let the user manually change it.
|
||||
"""
|
||||
if not self.env.user._is_admin():
|
||||
if not self.env.user._is_system():
|
||||
for invoice in self:
|
||||
invoice.sequence_number_next_prefix = False
|
||||
invoice.sequence_number_next = ''
|
||||
@@ -1523,6 +1523,13 @@ class AccountInvoiceLine(models.Model):
|
||||
return accounts['income']
|
||||
return accounts['expense']
|
||||
|
||||
def _set_currency(self):
|
||||
company = self.invoice_id.company_id
|
||||
currency = self.invoice_id.currency_id
|
||||
if company and currency:
|
||||
if company.currency_id != currency:
|
||||
self.price_unit = self.price_unit * currency.with_context(dict(self._context or {}, date=self.invoice_id.date_invoice)).rate
|
||||
|
||||
def _set_taxes(self):
|
||||
""" Used in on_change to set taxes and price."""
|
||||
if self.invoice_id.type in ('out_invoice', 'out_refund'):
|
||||
@@ -1541,8 +1548,10 @@ class AccountInvoiceLine(models.Model):
|
||||
prec = self.env['decimal.precision'].precision_get('Product Price')
|
||||
if not self.price_unit or float_compare(self.price_unit, self.product_id.standard_price, precision_digits=prec) == 0:
|
||||
self.price_unit = fix_price(self.product_id.standard_price, taxes, fp_taxes)
|
||||
self._set_currency()
|
||||
else:
|
||||
self.price_unit = fix_price(self.product_id.lst_price, taxes, fp_taxes)
|
||||
self._set_currency()
|
||||
|
||||
@api.onchange('product_id')
|
||||
def _onchange_product_id(self):
|
||||
@@ -1591,8 +1600,6 @@ class AccountInvoiceLine(models.Model):
|
||||
domain['uom_id'] = [('category_id', '=', product.uom_id.category_id.id)]
|
||||
|
||||
if company and currency:
|
||||
if company.currency_id != currency:
|
||||
self.price_unit = self.price_unit * currency.with_context(dict(self._context or {}, date=self.invoice_id.date_invoice)).rate
|
||||
|
||||
if self.uom_id and self.uom_id.id != product.uom_id.id:
|
||||
self.price_unit = product.uom_id._compute_price(self.price_unit, self.uom_id)
|
||||
|
||||
@@ -14,7 +14,7 @@ class ResConfigSettings(models.TransientModel):
|
||||
'account.journal',
|
||||
related='company_id.currency_exchange_journal_id',
|
||||
string="Exchange Gain or Loss Journal",
|
||||
domain=[('type', '=', 'general')],
|
||||
domain="[('company_id', '=', company_id), ('type', '=', 'general')]",
|
||||
help='The accounting journal where automatic exchange differences will be registered')
|
||||
has_chart_of_accounts = fields.Boolean(compute='_compute_has_chart_of_accounts', string='Company has a chart of accounts')
|
||||
chart_template_id = fields.Many2one('account.chart.template', string='Template',
|
||||
|
||||
@@ -248,14 +248,16 @@
|
||||
<span><t t-esc="dashboard.account_balance"/></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row" name="latest_statement" t-if="dashboard.last_balance != dashboard.account_balance">
|
||||
<div class="col-xs-7">
|
||||
<t t-if="dashboard.last_balance != dashboard.account_balance">
|
||||
<div class="row" name="latest_statement">
|
||||
<div class="col-xs-7">
|
||||
<span title="Latest Statement">Latest Statement</span>
|
||||
</div>
|
||||
<div class="col-xs-5 text-right">
|
||||
</div>
|
||||
<div class="col-xs-5 text-right">
|
||||
<span><t t-esc="dashboard.last_balance"/></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</div>
|
||||
</t>
|
||||
<t t-name="JournalBodySalePurchase" id="account.JournalBodySalePurchase">
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
<td><span t-field="aml.date"/></td>
|
||||
<td><span t-field="aml.account_id.code"/></td>
|
||||
<td><span t-esc="aml.sudo().partner_id and aml.sudo().partner_id.name and aml.sudo().partner_id.name[:23] or ''"/></td>
|
||||
<td><span t-esc="aml.name[:35]"/></td>
|
||||
<td><span t-esc="aml.name and aml.name[:35]"/></td>
|
||||
<td><span t-esc="aml.debit" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/></td>
|
||||
<td><span t-esc="aml.credit" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/></td>
|
||||
<td t-if="data['form']['amount_currency'] and aml.amount_currency">
|
||||
|
||||
@@ -50,7 +50,11 @@ class AccountBankStatementImport(models.TransientModel):
|
||||
# Create the bank statements
|
||||
statement_ids, notifications = self._create_bank_statements(stmts_vals)
|
||||
# Now that the import worked out, set it as the bank_statements_source of the journal
|
||||
journal.bank_statements_source = 'file_import'
|
||||
if journal.bank_statements_source != 'file_import':
|
||||
# Use sudo() because only 'account.group_account_manager'
|
||||
# has write access on 'account.journal', but 'account.group_account_user'
|
||||
# must be able to import bank statement files
|
||||
journal.sudo().bank_statements_source = 'file_import'
|
||||
# Finally dispatch to reconciliation interface
|
||||
action = self.env.ref('account.action_bank_reconcile_bank_statements')
|
||||
return {
|
||||
|
||||
@@ -8,13 +8,14 @@
|
||||
# Ayhan KIZILTAN <akiziltan76@hotmail.com>, 2018
|
||||
# Martin Trigaux, 2018
|
||||
# Alexander B. <road2monstercat@gmail.com>, 2018
|
||||
# Umur Akın <umura@projetgrup.com>, 2018
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Odoo Server saas~11.2\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2018-03-22 14:10+0000\n"
|
||||
"PO-Revision-Date: 2018-03-22 14:10+0000\n"
|
||||
"Last-Translator: Alexander B. <road2monstercat@gmail.com>, 2018\n"
|
||||
"Last-Translator: Umur Akın <umura@projetgrup.com>, 2018\n"
|
||||
"Language-Team: Turkish (https://www.transifex.com/odoo/teams/41243/tr/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@@ -333,6 +334,8 @@ msgid ""
|
||||
"The ISO country code in two chars. \n"
|
||||
"You can use this field for quick search."
|
||||
msgstr ""
|
||||
"İki karakterde ISO ülke kodu.\n"
|
||||
"Hızlı arama için bu alanı kullanabilirsiniz."
|
||||
|
||||
#. module: account_check_printing
|
||||
#: code:addons/account_check_printing/models/account_journal.py:26
|
||||
|
||||
@@ -10,7 +10,7 @@ from odoo.tools.translate import _
|
||||
class Partner(models.Model):
|
||||
_inherit = 'res.partner'
|
||||
|
||||
country_enforce_cities = fields.Boolean(related='country_id.enforce_cities')
|
||||
country_enforce_cities = fields.Boolean(related='country_id.enforce_cities', readonly=True)
|
||||
city_id = fields.Many2one('res.city', string='City of Address')
|
||||
|
||||
@api.onchange('city_id')
|
||||
|
||||
@@ -99,7 +99,10 @@ FavoriteMenu.include({
|
||||
})
|
||||
.then(function (r) {
|
||||
if (r) {
|
||||
self.do_notify(_.str.sprintf(_t("'%s' added to dashboard"), name), '');
|
||||
self.do_notify(
|
||||
_.str.sprintf(_t("'%s' added to dashboard"), name),
|
||||
_t('Please refresh your browser for the changes to take effect.')
|
||||
);
|
||||
} else {
|
||||
self.do_warn(_t("Could not add filter to dashboard"));
|
||||
}
|
||||
|
||||
@@ -237,6 +237,8 @@ class Lead(models.Model):
|
||||
@api.model
|
||||
def _onchange_user_values(self, user_id):
|
||||
""" returns new values when user_id has changed """
|
||||
if not user_id:
|
||||
return {}
|
||||
if user_id and self._context.get('team_id'):
|
||||
team = self.env['crm.team'].browse(self._context['team_id'])
|
||||
if user_id in team.member_ids.ids:
|
||||
|
||||
@@ -1551,7 +1551,7 @@ msgstr ""
|
||||
#. module: event
|
||||
#: model:ir.model,name:event.model_event_confirm
|
||||
msgid "event.confirm"
|
||||
msgstr ""
|
||||
msgstr "event.confirm"
|
||||
|
||||
#. module: event
|
||||
#: code:addons/event/models/event.py:503
|
||||
|
||||
@@ -444,9 +444,14 @@ class EventRegistration(models.Model):
|
||||
@api.multi
|
||||
def message_get_suggested_recipients(self):
|
||||
recipients = super(EventRegistration, self).message_get_suggested_recipients()
|
||||
public_users = self.env['res.users'].sudo()
|
||||
public_groups = self.env.ref("base.group_public", raise_if_not_found=False)
|
||||
if public_groups:
|
||||
public_users = public_groups.sudo().with_context(active_test=False).mapped("users")
|
||||
try:
|
||||
for attendee in self:
|
||||
if attendee.partner_id:
|
||||
is_public = attendee.sudo().with_context(active_test=False).partner_id.user_ids in public_users if public_users else False
|
||||
if attendee.partner_id and not is_public:
|
||||
attendee._message_add_suggested_recipient(recipients, partner=attendee.partner_id, reason=_('Customer'))
|
||||
elif attendee.email:
|
||||
attendee._message_add_suggested_recipient(recipients, email=attendee.email, reason=_('Customer Email'))
|
||||
|
||||
@@ -9,6 +9,8 @@ class ResPartner(models.Model):
|
||||
event_count = fields.Integer("Events", compute='_compute_event_count', help="Number of events the partner has participated.")
|
||||
|
||||
def _compute_event_count(self):
|
||||
if not self.user_has_groups('event.group_event_user'):
|
||||
return
|
||||
for partner in self:
|
||||
partner.event_count = self.env['event.event'].search_count([('registration_ids.partner_id', 'child_of', partner.ids)])
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
<img t-att-src="'data:image/png;base64,%s' % to_text(o.event_id.organizer_id.company_id.logo_web)" style="max-height:1cm; max-width:4cm;"/>
|
||||
</span>
|
||||
<h5 t-field="o.event_id.name"/>
|
||||
<h5>( <i class="fa fa-clock-o"></i> <span itemprop="startDate" t-field="o.event_id.date_begin" t-options='{"hide_seconds": True}'> </span> <i>to</i> <span itemprop="endDate" t-field="o.event_id.date_end" t-options='{"hide_seconds": True}'> </span> )</h5>
|
||||
<h5>( <i class="fa fa-clock-o"></i> <span itemprop="startDate" t-field="o.event_id.with_context(tz=o.event_id.date_tz).date_begin" t-options='{"hide_seconds": True}'> </span> <i>to</i> <span itemprop="endDate" t-field="o.event_id.with_context(tz=o.event_id.date_tz).date_end" t-options='{"hide_seconds": True}'> </span> )</h5>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xs-12 text-center" id="o_event_name">
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
<button class="oe_stat_button"
|
||||
type="object"
|
||||
icon="fa-ticket"
|
||||
groups="event.group_event_user"
|
||||
name="action_event_view" attrs="{'invisible': [('event_count','=', 0)]}">
|
||||
<field string="Events" name="event_count" widget="statinfo"/>
|
||||
</button>
|
||||
|
||||
@@ -455,7 +455,7 @@ msgstr "Lütfen kayıtlarla ilgili ayrıntıları veriniz."
|
||||
#. module: event_sale
|
||||
#: model:ir.model,name:event_sale.model_registration_editor
|
||||
msgid "registration.editor"
|
||||
msgstr ""
|
||||
msgstr "registration.editor"
|
||||
|
||||
#. module: event_sale
|
||||
#: model:ir.model,name:event_sale.model_registration_editor_line
|
||||
|
||||
@@ -158,6 +158,8 @@ msgstr "Sözleşmenin hala geçerli olup olmadığını seçin"
|
||||
msgid ""
|
||||
"Choose whether the service refer to contracts, vehicle services or both"
|
||||
msgstr ""
|
||||
"Hizmetin sözleşmelerle mi, araç hizmetleriyle mi ya da her ikisiyle de mi "
|
||||
"ilgili olduğunu seçin"
|
||||
|
||||
#. module: fleet
|
||||
#: model:ir.ui.view,arch_db:fleet.fleet_vehicle_log_contract_view_form
|
||||
|
||||
@@ -150,6 +150,15 @@
|
||||
<filter name="alert_true" domain="['|',('contract_renewal_due_soon','=',True),('contract_renewal_overdue','=',True)]" string="Has Alert(s)"/>
|
||||
<separator/>
|
||||
<filter string="Archived" name="inactive" domain="[('active','=',False)]"/>
|
||||
<separator/>
|
||||
<filter string="Late Activities" name="activities_overdue"
|
||||
domain="[('activity_ids.date_deadline', '<', context_today().strftime('%Y-%m-%d'))]"
|
||||
help="Show all records which has next action date is before today"/>
|
||||
<filter string="Today Activities" name="activities_today"
|
||||
domain="[('activity_ids.date_deadline', '=', context_today().strftime('%Y-%m-%d'))]"/>
|
||||
<filter string="Future Activities" name="activities_upcoming_all"
|
||||
domain="[('activity_ids.date_deadline', '>', context_today().strftime('%Y-%m-%d'))
|
||||
]"/>
|
||||
<group expand="1" string="Group By">
|
||||
<filter name="groupby_status" context="{'group_by' : 'state_id'}" string="Status"/>
|
||||
<filter name="groupby_model" context="{'group_by' : 'model_id'}" string="Model"/>
|
||||
@@ -163,7 +172,7 @@
|
||||
<field name="name">fleet.vehicle.kanban</field>
|
||||
<field name="model">fleet.vehicle</field>
|
||||
<field name="arch" type="xml">
|
||||
<kanban>
|
||||
<kanban default_group_by="state_id">
|
||||
<field name="license_plate" />
|
||||
<field name="model_id" />
|
||||
<field name="driver_id" />
|
||||
|
||||
@@ -292,7 +292,7 @@ msgstr "Tokenınız geçersiz ya da iptal edilmiş!"
|
||||
#. module: google_calendar
|
||||
#: model:ir.model,name:google_calendar.model_google_calendar
|
||||
msgid "google.calendar"
|
||||
msgstr ""
|
||||
msgstr "google.calendar"
|
||||
|
||||
#. module: google_calendar
|
||||
#: model:ir.model,name:google_calendar.model_res_config_settings
|
||||
|
||||
@@ -87,7 +87,7 @@ class HolidaysRequest(models.Model):
|
||||
number_of_days = fields.Float('Number of Days', compute='_compute_number_of_days', store=True, track_visibility='onchange')
|
||||
meeting_id = fields.Many2one('calendar.event', string='Meeting')
|
||||
|
||||
parent_id = fields.Many2one('hr.leave', string='Parent')
|
||||
parent_id = fields.Many2one('hr.leave', string='Parent', copy=False)
|
||||
linked_request_ids = fields.One2many('hr.leave', 'parent_id', string='Linked Requests')
|
||||
department_id = fields.Many2one('hr.department', string='Department', readonly=True, states={'draft': [('readonly', False)], 'confirm': [('readonly', False)]})
|
||||
category_id = fields.Many2one('hr.employee.category', string='Employee Tag', readonly=True,
|
||||
|
||||
@@ -12,13 +12,14 @@
|
||||
# danimaribeiro <danimaribeiro@gmail.com>, 2016
|
||||
# Adriel Kotviski <kotviski@gmail.com>, 2016
|
||||
# Silmar <pinheirosilmar@gmail.com>, 2017
|
||||
# Thiago Alves Cavalcante <tkramer.tk@gmail.com>, 2018
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Odoo Server saas~11.2\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2018-03-22 14:10+0000\n"
|
||||
"PO-Revision-Date: 2018-03-22 14:10+0000\n"
|
||||
"Last-Translator: Silmar <pinheirosilmar@gmail.com>, 2017\n"
|
||||
"Last-Translator: Thiago Alves Cavalcante <tkramer.tk@gmail.com>, 2018\n"
|
||||
"Language-Team: Portuguese (Brazil) (https://www.transifex.com/odoo/teams/41243/pt_BR/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@@ -414,7 +415,7 @@ msgstr "<span class=\"o_stat_text\">Rastreadores</span>"
|
||||
#. module: hr_recruitment
|
||||
#: model:ir.ui.view,arch_db:hr_recruitment.view_hr_recruitment_stage_kanban
|
||||
msgid "<span>Folded in Recruitment Pipe: </span>"
|
||||
msgstr ""
|
||||
msgstr "Dobrado na Lista de Recrutamento"
|
||||
|
||||
#. module: hr_recruitment
|
||||
#: model:ir.model.fields,help:hr_recruitment.field_hr_job__alias_defaults
|
||||
@@ -484,7 +485,7 @@ msgstr "Modelo Apelidado"
|
||||
#. module: hr_recruitment
|
||||
#: model:ir.ui.menu,name:hr_recruitment.menu_crm_case_categ_all_app
|
||||
msgid "All Applications"
|
||||
msgstr ""
|
||||
msgstr "Todas as aplicações"
|
||||
|
||||
#. module: hr_recruitment
|
||||
#: model:ir.model,name:hr_recruitment.model_hr_applicant
|
||||
@@ -608,7 +609,7 @@ msgstr "Anexos"
|
||||
#. module: hr_recruitment
|
||||
#: model:ir.model.fields,field_description:hr_recruitment.field_hr_recruitment_stage__template_id
|
||||
msgid "Automated Email"
|
||||
msgstr ""
|
||||
msgstr "E-mail Automatizado"
|
||||
|
||||
#. module: hr_recruitment
|
||||
#: model:ir.model.fields,field_description:hr_recruitment.field_hr_applicant__availability
|
||||
@@ -650,7 +651,7 @@ msgstr "Categoria de candidato"
|
||||
#: code:addons/hr_recruitment/static/src/js/tour.js:16
|
||||
#, python-format
|
||||
msgid "Click here to create a new job position."
|
||||
msgstr ""
|
||||
msgstr "Clique aqui para criar um novo cargo."
|
||||
|
||||
#. module: hr_recruitment
|
||||
#: model:ir.model.fields,field_description:hr_recruitment.field_hr_applicant__date_closed
|
||||
@@ -722,7 +723,7 @@ msgstr "Criar Funcionário"
|
||||
#: model:ir.actions.act_window,name:hr_recruitment.create_job_simple
|
||||
#: model:ir.ui.view,arch_db:hr_recruitment.hr_job_simple_form
|
||||
msgid "Create a Job Position"
|
||||
msgstr ""
|
||||
msgstr "Criar um Cargo"
|
||||
|
||||
#. module: hr_recruitment
|
||||
#: model:ir.ui.view,arch_db:hr_recruitment.hr_recruitment_source_tree
|
||||
@@ -915,7 +916,7 @@ msgstr "Apelido de E-mail"
|
||||
#. module: hr_recruitment
|
||||
#: model:ir.ui.view,arch_db:hr_recruitment.hr_job_survey
|
||||
msgid "Email alias"
|
||||
msgstr ""
|
||||
msgstr "Apelido de email"
|
||||
|
||||
#. module: hr_recruitment
|
||||
#: model:ir.model.fields,help:hr_recruitment.field_hr_job__alias_id
|
||||
@@ -1039,7 +1040,7 @@ msgstr "Departamento de RH"
|
||||
#. module: hr_recruitment
|
||||
#: model:ir.model.fields,field_description:hr_recruitment.field_hr_job__hr_responsible_id
|
||||
msgid "HR Responsible"
|
||||
msgstr ""
|
||||
msgstr "Responsável do RH"
|
||||
|
||||
#. module: hr_recruitment
|
||||
#: model:ir.ui.view,arch_db:hr_recruitment.view_hr_job_kanban
|
||||
@@ -1091,7 +1092,7 @@ msgstr "Qualificação Inicial"
|
||||
#: model:ir.model.fields,field_description:hr_recruitment.field_res_config_settings__module_hr_recruitment_survey
|
||||
#: model:ir.ui.view,arch_db:hr_recruitment.res_config_settings_view_form
|
||||
msgid "Interview Forms"
|
||||
msgstr ""
|
||||
msgstr "Formulários de Entrevista"
|
||||
|
||||
#. module: hr_recruitment
|
||||
#: code:addons/hr_recruitment/models/hr_job.py:94
|
||||
@@ -1104,12 +1105,12 @@ msgstr "Trabalho"
|
||||
#. module: hr_recruitment
|
||||
#: model:mail.template,subject:hr_recruitment.email_template_data_applicant_congratulations
|
||||
msgid "Job Application Confirmation: ${object.job_id.name | safe}"
|
||||
msgstr ""
|
||||
msgstr "Confirmação de pedido de emprego: ${object.job_id.name | safe}"
|
||||
|
||||
#. module: hr_recruitment
|
||||
#: model:ir.ui.view,arch_db:hr_recruitment.crm_case_pivot_view_job
|
||||
msgid "Job Applications"
|
||||
msgstr ""
|
||||
msgstr "Solicitações de emprego"
|
||||
|
||||
#. module: hr_recruitment
|
||||
#: model:utm.campaign,name:hr_recruitment.utm_campaign_job
|
||||
@@ -1152,12 +1153,12 @@ msgstr "Cargos"
|
||||
#. module: hr_recruitment
|
||||
#: model:ir.ui.view,arch_db:hr_recruitment.res_config_settings_view_form
|
||||
msgid "Job Posting"
|
||||
msgstr ""
|
||||
msgstr "Anúncio de emprego"
|
||||
|
||||
#. module: hr_recruitment
|
||||
#: model:ir.model.fields,field_description:hr_recruitment.field_hr_recruitment_stage__job_id
|
||||
msgid "Job Specific"
|
||||
msgstr ""
|
||||
msgstr "Especificação de trabalho"
|
||||
|
||||
#. module: hr_recruitment
|
||||
#: model:ir.ui.view,arch_db:hr_recruitment.hr_applicant_view_search
|
||||
@@ -1228,7 +1229,7 @@ msgstr ""
|
||||
#, python-format
|
||||
msgid ""
|
||||
"Let's have a look at the <b>applications pipeline</b> for this job position."
|
||||
msgstr ""
|
||||
msgstr "Vamos dar uma olhada na <b>lista de exigências</b> para este cargo. "
|
||||
|
||||
#. module: hr_recruitment
|
||||
#: model:res.groups,name:hr_recruitment.group_hr_recruitment_manager
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
<field name="manager_id"/>
|
||||
<field name="state"/>
|
||||
<field name="user_id"/>
|
||||
<field name="documents_count"/>
|
||||
<templates>
|
||||
<t t-name="kanban-box">
|
||||
<div t-attf-class="#{kanban_color(record.color.raw_value)}">
|
||||
@@ -49,7 +48,6 @@
|
||||
<div class="o_recruitment_kanban_box o_kanban_primary_bottom bottom_block">
|
||||
<a name="action_get_attachment_tree_view" type="object">
|
||||
<span title='Documents'><i class='fa fa-book'/>
|
||||
<t t-esc="record.documents_count.raw_value"/>
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@@ -5,17 +5,18 @@
|
||||
# Translators:
|
||||
# grazziano <gra.negocia@gmail.com>, 2016
|
||||
# Clemilton Clementino <clemylton@hotmail.com>, 2016
|
||||
# Martin Trigaux <mat@odoo.com>, 2016
|
||||
# Martin Trigaux, 2016
|
||||
# Mateus Lopes <mateus1@gmail.com>, 2016
|
||||
# danimaribeiro <danimaribeiro@gmail.com>, 2016
|
||||
# Rodrigo de Almeida Sottomaior Macedo <rmsolucoeseminformatic4@gmail.com>, 2016
|
||||
# Thiago Alves Cavalcante <tkramer.tk@gmail.com>, 2018
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Odoo Server 10.saas~18\n"
|
||||
"Project-Id-Version: Odoo Server saas~11.2\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2017-09-20 09:53+0000\n"
|
||||
"PO-Revision-Date: 2017-09-20 09:53+0000\n"
|
||||
"Last-Translator: Rodrigo de Almeida Sottomaior Macedo <rmsolucoeseminformatic4@gmail.com>, 2016\n"
|
||||
"POT-Creation-Date: 2018-03-22 14:10+0000\n"
|
||||
"PO-Revision-Date: 2018-03-22 14:10+0000\n"
|
||||
"Last-Translator: Thiago Alves Cavalcante <tkramer.tk@gmail.com>, 2018\n"
|
||||
"Language-Team: Portuguese (Brazil) (https://www.transifex.com/odoo/teams/41243/pt_BR/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@@ -102,8 +103,8 @@ msgid "Basic information"
|
||||
msgstr "Informações básicas"
|
||||
|
||||
#. module: hr_recruitment_survey
|
||||
#: model:ir.model.fields,help:hr_recruitment_survey.field_hr_applicant_survey_id
|
||||
#: model:ir.model.fields,help:hr_recruitment_survey.field_hr_job_survey_id
|
||||
#: model:ir.model.fields,help:hr_recruitment_survey.field_hr_applicant__survey_id
|
||||
#: model:ir.model.fields,help:hr_recruitment_survey.field_hr_job__survey_id
|
||||
msgid ""
|
||||
"Choose an interview form for this job position and you will be able to "
|
||||
"print/answer this interview from all applicants who apply for this job"
|
||||
@@ -201,7 +202,7 @@ msgid "Important"
|
||||
msgstr "Importante"
|
||||
|
||||
#. module: hr_recruitment_survey
|
||||
#: model:ir.model.fields,field_description:hr_recruitment_survey.field_hr_job_survey_id
|
||||
#: model:ir.model.fields,field_description:hr_recruitment_survey.field_hr_job__survey_id
|
||||
#: model:ir.ui.view,arch_db:hr_recruitment_survey.view_hr_job_kanban_inherit
|
||||
msgid "Interview Form"
|
||||
msgstr "Formulário de Entrevista"
|
||||
@@ -209,7 +210,7 @@ msgstr "Formulário de Entrevista"
|
||||
#. module: hr_recruitment_survey
|
||||
#: model:ir.ui.view,arch_db:hr_recruitment_survey.res_config_settings_view_form
|
||||
msgid "Interview Forms"
|
||||
msgstr ""
|
||||
msgstr "Formulários de Entrevista"
|
||||
|
||||
#. module: hr_recruitment_survey
|
||||
#: model:ir.model,name:hr_recruitment_survey.model_hr_job
|
||||
@@ -282,7 +283,7 @@ msgid "Regular meetings"
|
||||
msgstr "Reuniões regulares"
|
||||
|
||||
#. module: hr_recruitment_survey
|
||||
#: model:ir.model.fields,field_description:hr_recruitment_survey.field_hr_applicant_response_id
|
||||
#: model:ir.model.fields,field_description:hr_recruitment_survey.field_hr_applicant__response_id
|
||||
msgid "Response"
|
||||
msgstr "Resposta"
|
||||
|
||||
@@ -297,7 +298,7 @@ msgid "State of the art technology"
|
||||
msgstr "Estado da Arte da Tecnologia"
|
||||
|
||||
#. module: hr_recruitment_survey
|
||||
#: model:ir.model.fields,field_description:hr_recruitment_survey.field_hr_applicant_survey_id
|
||||
#: model:ir.model.fields,field_description:hr_recruitment_survey.field_hr_applicant__survey_id
|
||||
msgid "Survey"
|
||||
msgstr "Pesquisa"
|
||||
|
||||
|
||||
@@ -86,7 +86,7 @@ msgstr "Kullanıcı"
|
||||
#. module: hr_timesheet_attendance
|
||||
#: model:ir.model,name:hr_timesheet_attendance.model_hr_timesheet_attendance_report
|
||||
msgid "hr.timesheet.attendance.report"
|
||||
msgstr ""
|
||||
msgstr "hr.timesheet.attendance.report"
|
||||
|
||||
#. module: hr_timesheet_attendance
|
||||
#: model:ir.ui.view,arch_db:hr_timesheet_attendance.view_hr_timesheet_attendance_report_pivot
|
||||
|
||||
@@ -130,4 +130,4 @@ msgstr "Hizmet Adı"
|
||||
#. module: iap
|
||||
#: model:ir.model,name:iap.model_iap_account
|
||||
msgid "iap.account"
|
||||
msgstr ""
|
||||
msgstr "iap.account"
|
||||
|
||||
@@ -64,6 +64,11 @@ msgid ""
|
||||
"country, GeoIP must be installed on your server, otherwise, the countries of"
|
||||
" the rule will not be taken into account.</span>"
|
||||
msgstr ""
|
||||
"<span>Canlı destek kanalınız için kuralları tanımlayın. Belirtilen URL ve "
|
||||
"ülke için aksiyon tanımlayabilir (desteği gizle, desteği otomatik aç, veya "
|
||||
"sadece butonu göster)<br/>Ülkeyi tanımlayabilmek için, GeoIP'nin sunucuda "
|
||||
"yüklenmiş olması gerekmektedir, aksi durumda ülke ile ilgili kurallar "
|
||||
"geçersiz olacaktır.</span>"
|
||||
|
||||
#. module: im_livechat
|
||||
#: model:ir.model.fields,field_description:im_livechat.field_im_livechat_channel_rule__action
|
||||
|
||||
@@ -593,7 +593,7 @@
|
||||
<field name="refund_account_id" ref="vat_refund_h_non_eu"/>
|
||||
<field name="account_id" ref="vat_refund_h_non_eu"/>
|
||||
<field name="type_tax_use">none</field>
|
||||
<field name="tag_ids" eval="[(6,0,[ref('tag_nl_34')])]"/>
|
||||
<field name="tag_ids" eval="[(6,0,[ref('tag_nl_33')])]"/>
|
||||
<field name="tax_group_id" ref="tax_group_21_niet_eu"/>
|
||||
</record>
|
||||
<record id="btw_E2" model="account.tax.template">
|
||||
@@ -735,7 +735,7 @@
|
||||
<field name="refund_account_id" ref="vat_refund_h_d_non_eu"/>
|
||||
<field name="account_id" ref="vat_refund_h_d_non_eu"/>
|
||||
<field name="type_tax_use">none</field>
|
||||
<field name="tag_ids" eval="[(6,0,[ref('tag_nl_34')])]"/>
|
||||
<field name="tag_ids" eval="[(6,0,[ref('tag_nl_33')])]"/>
|
||||
<field name="tax_group_id" ref="tax_group_21_niet_eu"/>
|
||||
</record>
|
||||
<record id="btw_E2_d" model="account.tax.template">
|
||||
|
||||
@@ -228,19 +228,19 @@ msgstr "Website Link Tıklamaları"
|
||||
#. module: link_tracker
|
||||
#: model:ir.model,name:link_tracker.model_link_tracker
|
||||
msgid "link.tracker"
|
||||
msgstr ""
|
||||
msgstr "link.tracker"
|
||||
|
||||
#. module: link_tracker
|
||||
#: model:ir.model,name:link_tracker.model_link_tracker_click
|
||||
msgid "link.tracker.click"
|
||||
msgstr ""
|
||||
msgstr "link.tracker.click"
|
||||
|
||||
#. module: link_tracker
|
||||
#: model:ir.model,name:link_tracker.model_link_tracker_code
|
||||
msgid "link.tracker.code"
|
||||
msgstr ""
|
||||
msgstr "link.tracker.code"
|
||||
|
||||
#. module: link_tracker
|
||||
#: model:ir.actions.act_window,name:link_tracker.action_link_tracker_stats
|
||||
msgid "link.tracker.form.graph.action"
|
||||
msgstr ""
|
||||
msgstr "link.tracker.form.graph.action"
|
||||
|
||||
@@ -986,4 +986,4 @@ msgstr "öğle yemeği ürün kategorisi"
|
||||
#. module: lunch
|
||||
#: model:ir.model,name:lunch.model_lunch_order_line_lucky
|
||||
msgid "lunch.order.line.lucky"
|
||||
msgstr ""
|
||||
msgstr "lunch.order.line.lucky"
|
||||
|
||||
+39
-28
@@ -329,6 +329,8 @@ msgid ""
|
||||
"A shortcode is a keyboard shortcut. For instance, you type #gm and it will "
|
||||
"be transformed into \"Good Morning\"."
|
||||
msgstr ""
|
||||
"Bir kısa kod, bir klavye kısayoludır. Örneğin, # gm yazarsınız ve "
|
||||
"\"Günaydın\" a dönüşür."
|
||||
|
||||
#. module: mail
|
||||
#: model:ir.model,name:mail.model_res_groups
|
||||
@@ -1518,6 +1520,8 @@ msgid ""
|
||||
"Email address of the sender. This field is set when no matching partner is "
|
||||
"found and replaces the author_id field in the chatter."
|
||||
msgstr ""
|
||||
"Gönderenin e-posta adresi. Eşleşen bir ortak bulunmadığında ve sohbetteki "
|
||||
"author_id alanının yerini aldığında bu alan ayarlanır."
|
||||
|
||||
#. module: mail
|
||||
#: model:ir.ui.view,arch_db:mail.email_compose_message_wizard_form
|
||||
@@ -3123,7 +3127,7 @@ msgstr ""
|
||||
#. module: mail
|
||||
#: model:ir.model.fields,field_description:mail.field_mail_tracking_value__new_value_char
|
||||
msgid "New Value Char"
|
||||
msgstr ""
|
||||
msgstr "Yeni Karakter Değeri"
|
||||
|
||||
#. module: mail
|
||||
#: model:ir.model.fields,field_description:mail.field_mail_tracking_value__new_value_datetime
|
||||
@@ -3143,12 +3147,12 @@ msgstr "Yeni Tam Sayı Değeri"
|
||||
#. module: mail
|
||||
#: model:ir.model.fields,field_description:mail.field_mail_tracking_value__new_value_monetary
|
||||
msgid "New Value Monetary"
|
||||
msgstr ""
|
||||
msgstr "Yeni Parasal Değer"
|
||||
|
||||
#. module: mail
|
||||
#: model:ir.model.fields,field_description:mail.field_mail_tracking_value__new_value_text
|
||||
msgid "New Value Text"
|
||||
msgstr ""
|
||||
msgstr "Yeni Metin Değeri"
|
||||
|
||||
#. module: mail
|
||||
#. openerp-web
|
||||
@@ -3275,7 +3279,7 @@ msgstr ""
|
||||
#: code:addons/mail/static/src/xml/web_kanban_activity.xml:22
|
||||
#, python-format
|
||||
msgid "No activities planned."
|
||||
msgstr ""
|
||||
msgstr "Hiçbir etkinlik planlanmadı."
|
||||
|
||||
#. module: mail
|
||||
#. openerp-web
|
||||
@@ -3591,7 +3595,7 @@ msgstr "Odoo"
|
||||
#, python-format
|
||||
msgid ""
|
||||
"Odoo has now the permission to send you native notifications on this device."
|
||||
msgstr ""
|
||||
msgstr "Odoo, artık bu cihazda yerel bildirimler göndermek için izin aldı."
|
||||
|
||||
#. module: mail
|
||||
#. openerp-web
|
||||
@@ -3607,7 +3611,7 @@ msgstr "Odoo için izniniz gerekiyor"
|
||||
msgid ""
|
||||
"Odoo will not have the permission to send native notifications on this "
|
||||
"device."
|
||||
msgstr ""
|
||||
msgstr "Odoo, bu cihazda yerel bildirimler gönderme iznine sahip olmayacak."
|
||||
|
||||
#. module: mail
|
||||
#. openerp-web
|
||||
@@ -3619,32 +3623,32 @@ msgstr "Çevrimdışı"
|
||||
#. module: mail
|
||||
#: model:ir.model.fields,field_description:mail.field_mail_tracking_value__old_value_char
|
||||
msgid "Old Value Char"
|
||||
msgstr ""
|
||||
msgstr "Eski Karakter Değeri"
|
||||
|
||||
#. module: mail
|
||||
#: model:ir.model.fields,field_description:mail.field_mail_tracking_value__old_value_datetime
|
||||
msgid "Old Value DateTime"
|
||||
msgstr ""
|
||||
msgstr "Eski Tarih Saat Değeri"
|
||||
|
||||
#. module: mail
|
||||
#: model:ir.model.fields,field_description:mail.field_mail_tracking_value__old_value_float
|
||||
msgid "Old Value Float"
|
||||
msgstr ""
|
||||
msgstr "Eski Sayısal Değer"
|
||||
|
||||
#. module: mail
|
||||
#: model:ir.model.fields,field_description:mail.field_mail_tracking_value__old_value_integer
|
||||
msgid "Old Value Integer"
|
||||
msgstr ""
|
||||
msgstr "Eski Tam Sayı Değeri"
|
||||
|
||||
#. module: mail
|
||||
#: model:ir.model.fields,field_description:mail.field_mail_tracking_value__old_value_monetary
|
||||
msgid "Old Value Monetary"
|
||||
msgstr ""
|
||||
msgstr "Eski Parasal Değer"
|
||||
|
||||
#. module: mail
|
||||
#: model:ir.model.fields,field_description:mail.field_mail_tracking_value__old_value_text
|
||||
msgid "Old Value Text"
|
||||
msgstr ""
|
||||
msgstr "Eski Metin Değeri"
|
||||
|
||||
#. module: mail
|
||||
#: selection:ir.model.fields,track_visibility:0
|
||||
@@ -3701,21 +3705,21 @@ msgstr "Üst Belgeyi Aç"
|
||||
#: code:addons/mail/static/src/xml/discuss.xml:139
|
||||
#, python-format
|
||||
msgid "Open channel settings"
|
||||
msgstr ""
|
||||
msgstr "Açık kanal ayarları"
|
||||
|
||||
#. module: mail
|
||||
#. openerp-web
|
||||
#: code:addons/mail/static/src/xml/discuss.xml:187
|
||||
#, python-format
|
||||
msgid "Open chat"
|
||||
msgstr ""
|
||||
msgstr "Konuşmayı aç"
|
||||
|
||||
#. module: mail
|
||||
#. openerp-web
|
||||
#: code:addons/mail/static/src/xml/extended_chat_window.xml:15
|
||||
#, python-format
|
||||
msgid "Open in Discuss"
|
||||
msgstr ""
|
||||
msgstr "Tartışmayı Aç"
|
||||
|
||||
#. module: mail
|
||||
#: model:ir.model.fields,field_description:mail.field_res_partner__opt_out
|
||||
@@ -3881,6 +3885,9 @@ msgid ""
|
||||
" named. For example on a project, the parent_id of project subtypes refers "
|
||||
"to task-related subtypes."
|
||||
msgstr ""
|
||||
"Otomatik abonelik için kullanılan alt alt türü. Bu alan doğru bir şekilde "
|
||||
"adlandırılmamış. Örneğin, bir projede, proje alt türlerinin parent_id'i, "
|
||||
"görevle ilgili alt türlere başvurur."
|
||||
|
||||
#. module: mail
|
||||
#: model:ir.actions.act_window,name:mail.action_partner_mass_mail
|
||||
@@ -3905,7 +3912,7 @@ msgstr ""
|
||||
#: model:ir.model.fields,field_description:mail.field_mail_message__needaction_partner_ids
|
||||
#: model:ir.model.fields,field_description:mail.field_survey_mail_compose_message__needaction_partner_ids
|
||||
msgid "Partners with Need Action"
|
||||
msgstr ""
|
||||
msgstr "Eylem İhtiyacı Olan İş Ortakları"
|
||||
|
||||
#. module: mail
|
||||
#: model:ir.model.fields,help:mail.field_email_template_preview__auto_delete
|
||||
@@ -4312,7 +4319,7 @@ msgstr "Yeni Şablon olarak kaydet"
|
||||
#: code:addons/mail/static/src/js/chat_window.js:40
|
||||
#, python-format
|
||||
msgid "Say something"
|
||||
msgstr ""
|
||||
msgstr "Bir şey söyle"
|
||||
|
||||
#. module: mail
|
||||
#: model:ir.ui.view,arch_db:mail.mail_activity_view_form_popup
|
||||
@@ -4436,7 +4443,7 @@ msgstr ""
|
||||
#: code:addons/mail/static/src/js/thread_field.js:92
|
||||
#, python-format
|
||||
msgid "Sending Error"
|
||||
msgstr ""
|
||||
msgstr "Hata Gönderiliyor"
|
||||
|
||||
#. module: mail
|
||||
#. openerp-web
|
||||
@@ -4483,7 +4490,7 @@ msgstr ""
|
||||
#: model:ir.ui.view,arch_db:mail.mail_shortcode_view_form
|
||||
#: model:ir.ui.view,arch_db:mail.mail_shortcode_view_tree
|
||||
msgid "Shortcodes"
|
||||
msgstr ""
|
||||
msgstr "Kısa kodlar"
|
||||
|
||||
#. module: mail
|
||||
#: model:ir.model.fields,field_description:mail.field_mail_shortcode__source
|
||||
@@ -4500,7 +4507,7 @@ msgstr "Bir sonraki eylem tarihi bugünden önce olan tüm kayıtları göster"
|
||||
#: code:addons/mail/models/mail_channel.py:673
|
||||
#, python-format
|
||||
msgid "Show an helper message"
|
||||
msgstr ""
|
||||
msgstr "Bir yardımcı mesajı göster"
|
||||
|
||||
#. module: mail
|
||||
#: model:ir.model.fields,field_description:mail.field_email_template_preview__ref_ir_act_window
|
||||
@@ -4557,6 +4564,8 @@ msgid ""
|
||||
"Specify a model if the activity should be specific to a model and not "
|
||||
"available when managing activities for other models."
|
||||
msgstr ""
|
||||
"Etkinliğin bir modele özgü olması ve diğer modellerin etkinliklerini "
|
||||
"yönetirken kullanılamaması durumunda bir model belirtin."
|
||||
|
||||
#. module: mail
|
||||
#. openerp-web
|
||||
@@ -4821,6 +4830,8 @@ msgid ""
|
||||
"This group is visible by non members. Invisible groups can add members "
|
||||
"through the invite button."
|
||||
msgstr ""
|
||||
"Bu grup üye olmayanlar tarafından görülebilir. Görünmez gruplar, davet "
|
||||
"butonuyla üye ekleyebilir."
|
||||
|
||||
#. module: mail
|
||||
#: model:ir.ui.view,arch_db:mail.view_mail_search
|
||||
@@ -4924,13 +4935,13 @@ msgstr "İzleme"
|
||||
#: model:ir.ui.view,arch_db:mail.view_mail_tracking_value_form
|
||||
#: model:ir.ui.view,arch_db:mail.view_mail_tracking_value_tree
|
||||
msgid "Tracking Value"
|
||||
msgstr ""
|
||||
msgstr "İzleme Değeri"
|
||||
|
||||
#. module: mail
|
||||
#: model:ir.actions.act_window,name:mail.action_view_mail_tracking_value
|
||||
#: model:ir.ui.menu,name:mail.menu_mail_tracking_value
|
||||
msgid "Tracking Values"
|
||||
msgstr ""
|
||||
msgstr "İzleme Değerleri"
|
||||
|
||||
#. module: mail
|
||||
#: model:ir.model.fields,field_description:mail.field_mail_compose_message__tracking_value_ids
|
||||
@@ -4938,7 +4949,7 @@ msgstr ""
|
||||
#: model:ir.model.fields,field_description:mail.field_mail_message__tracking_value_ids
|
||||
#: model:ir.model.fields,field_description:mail.field_survey_mail_compose_message__tracking_value_ids
|
||||
msgid "Tracking values"
|
||||
msgstr ""
|
||||
msgstr "İzleme Değerleri"
|
||||
|
||||
#. module: mail
|
||||
#: model:ir.model.fields,field_description:mail.field_mail_compose_message__message_type
|
||||
@@ -5191,7 +5202,7 @@ msgstr "Şablonu Kullan"
|
||||
#. module: mail
|
||||
#: model:ir.ui.view,arch_db:mail.res_config_settings_view_form
|
||||
msgid "Use your own email servers"
|
||||
msgstr ""
|
||||
msgstr "Kendi e-mail sunucusunu kullanın"
|
||||
|
||||
#. module: mail
|
||||
#: model:ir.model.fields,help:mail.field_mail_message_subtype__sequence
|
||||
@@ -5299,7 +5310,7 @@ msgstr "Bu modelin ileti ve bildirimleri destekleyip desteklemediğidir."
|
||||
#. module: mail
|
||||
#: model:ir.ui.view,arch_db:mail.mail_channel_view_form
|
||||
msgid "Who can follow the group's activities?"
|
||||
msgstr ""
|
||||
msgstr "Grubun faaliyetlerini kim takip edebilir?"
|
||||
|
||||
#. module: mail
|
||||
#. openerp-web
|
||||
@@ -5433,13 +5444,13 @@ msgstr ""
|
||||
#: code:addons/mail/static/src/js/thread_field.js:92
|
||||
#, python-format
|
||||
msgid "Your message has not been sent."
|
||||
msgstr ""
|
||||
msgstr "Mesajınız gönderilemedi."
|
||||
|
||||
#. module: mail
|
||||
#: code:addons/mail/models/ir_actions.py:30
|
||||
#, python-format
|
||||
msgid "Your template should define email_from"
|
||||
msgstr ""
|
||||
msgstr "Şablonunuz email_from tanımlanabilir"
|
||||
|
||||
#. module: mail
|
||||
#: code:addons/mail/models/mail_thread.py:948
|
||||
@@ -5533,7 +5544,7 @@ msgstr ""
|
||||
#: code:addons/mail/models/mail_alias.py:267
|
||||
#, python-format
|
||||
msgid "incorrectly configured alias (unknown reference record)"
|
||||
msgstr ""
|
||||
msgstr "yanlış yapılandırılmış takma ad (bilinmeyen referans kaydı)"
|
||||
|
||||
#. module: mail
|
||||
#: model:ir.model,name:mail.model_ir_actions_server
|
||||
|
||||
@@ -1620,7 +1620,7 @@ var ChatManager = AbstractService.extend({
|
||||
}
|
||||
this._removeChannel(channel);
|
||||
this.chatBus.trigger("unsubscribe_from_channel", data.id);
|
||||
web_client.do_notify(_("Unsubscribed"), msg);
|
||||
web_client.do_notify(_t("Unsubscribed"), msg);
|
||||
}
|
||||
} else if (data.type === 'toggle_star') {
|
||||
this._manageToggleStarNotification(data);
|
||||
|
||||
@@ -128,7 +128,7 @@ var MessagingMenu = Widget.extend({
|
||||
if (channelID === 'channel_inbox') {
|
||||
var resID = $(event.currentTarget).data('res_id');
|
||||
var resModel = $(event.currentTarget).data('res_model');
|
||||
if (resModel && resID) {
|
||||
if (resModel && resModel !== 'mail.channel' && resID) {
|
||||
this.do_action({
|
||||
type: 'ir.actions.act_window',
|
||||
res_model: resModel,
|
||||
@@ -136,7 +136,11 @@ var MessagingMenu = Widget.extend({
|
||||
res_id: resID
|
||||
});
|
||||
} else {
|
||||
this.do_action('mail.mail_channel_action_client_chat', {clear_breadcrumbs: true})
|
||||
var clientChatOptions = {clear_breadcrumbs: true};
|
||||
if (resModel && resModel === 'mail.channel' && resID) {
|
||||
clientChatOptions.active_id = resID;
|
||||
}
|
||||
this.do_action('mail.mail_channel_action_client_chat', clientChatOptions)
|
||||
.then(function () {
|
||||
self.trigger_up('hide_home_menu'); // we cannot 'go back to previous page' otherwise
|
||||
core.bus.trigger('change_menu_section', self.call('chat_manager', 'getDiscussMenuID'));
|
||||
|
||||
@@ -982,7 +982,7 @@ msgstr "Durumu değişti"
|
||||
#. module: maintenance
|
||||
#: model:ir.model.fields,field_description:maintenance.field_maintenance_request__name
|
||||
msgid "Subjects"
|
||||
msgstr ""
|
||||
msgstr "Konular"
|
||||
|
||||
#. module: maintenance
|
||||
#: model:ir.model.fields,field_description:maintenance.field_maintenance_request__maintenance_team_id
|
||||
|
||||
@@ -1911,7 +1911,7 @@ msgstr "Ve bir sonraki siparişinizde20 TL tasarruf edin!"
|
||||
#. module: mass_mailing
|
||||
#: model:ir.model.fields,field_description:mass_mailing.field_mail_mass_mailing_campaign__campaign_id
|
||||
msgid "campaign_id"
|
||||
msgstr ""
|
||||
msgstr "campaign_id"
|
||||
|
||||
#. module: mass_mailing
|
||||
#: model:ir.ui.view,arch_db:mass_mailing.view_mail_mass_mailing_list_form
|
||||
@@ -1937,12 +1937,12 @@ msgstr ""
|
||||
#. module: mass_mailing
|
||||
#: model:ir.model,name:mass_mailing.model_link_tracker
|
||||
msgid "link.tracker"
|
||||
msgstr ""
|
||||
msgstr "link.tracker"
|
||||
|
||||
#. module: mass_mailing
|
||||
#: model:ir.model,name:mass_mailing.model_link_tracker_click
|
||||
msgid "link.tracker.click"
|
||||
msgstr ""
|
||||
msgstr "link.tracker.click"
|
||||
|
||||
#. module: mass_mailing
|
||||
#: model:ir.model,name:mass_mailing.model_res_config_settings
|
||||
|
||||
@@ -41,7 +41,7 @@ FieldTextHtml.include({
|
||||
var datarecord = this._super();
|
||||
if (this.model === 'mail.mass_mailing') {
|
||||
// these fields can potentially get very long, let's remove them
|
||||
datarecord = _.omit(datarecord, ['mailing_domain', 'contact_list_ids', 'body_html']);
|
||||
datarecord = _.omit(datarecord, ['mailing_domain', 'contact_list_ids', 'body_html', 'attachment_ids']);
|
||||
}
|
||||
return datarecord;
|
||||
},
|
||||
|
||||
@@ -16,7 +16,7 @@ class Event(models.Model):
|
||||
view_mode='form',
|
||||
target='current',
|
||||
context=dict(
|
||||
default_mailing_model=self.env.ref('website_event_track.model_event_track').id,
|
||||
default_mailing_model_id=self.env.ref('website_event_track.model_event_track').id,
|
||||
default_mailing_domain="[('event_id', 'in', %s), ('stage_id.is_cancel', '!=', True)]" % self.ids,
|
||||
),
|
||||
)
|
||||
|
||||
@@ -80,6 +80,8 @@ class MrpBom(models.Model):
|
||||
def onchange_product_tmpl_id(self):
|
||||
if self.product_tmpl_id:
|
||||
self.product_uom_id = self.product_tmpl_id.uom_id.id
|
||||
if self.product_id.product_tmpl_id != self.product_tmpl_id:
|
||||
self.product_id = False
|
||||
|
||||
@api.onchange('routing_id')
|
||||
def onchange_routing_id(self):
|
||||
|
||||
@@ -364,6 +364,7 @@ class MrpWorkorder(models.Model):
|
||||
move_line = production_move.move_line_ids.filtered(lambda x: x.lot_id.id == self.final_lot_id.id)
|
||||
if move_line:
|
||||
move_line.product_uom_qty += self.qty_producing
|
||||
move_line.qty_done += self.qty_producing
|
||||
else:
|
||||
move_line.create({'move_id': production_move.id,
|
||||
'product_id': production_move.product_id.id,
|
||||
|
||||
@@ -73,6 +73,13 @@ class StockMove(models.Model):
|
||||
order_finished_lot_ids = fields.Many2many('stock.production.lot', compute='_compute_order_finished_lot_ids')
|
||||
finished_lots_exist = fields.Boolean('Finished Lots Exist', compute='_compute_order_finished_lot_ids')
|
||||
|
||||
def _unreserve_initial_demand(self, new_move):
|
||||
# If you were already putting stock.move.lots on the next one in the work order, transfer those to the new move
|
||||
self.filtered(lambda m: m.production_id or m.raw_material_production_id)\
|
||||
.mapped('move_line_ids')\
|
||||
.filtered(lambda ml: ml.qty_done == 0.0)\
|
||||
.write({'move_id': new_move, 'product_uom_qty': 0})
|
||||
|
||||
@api.depends('active_move_line_ids.qty_done', 'active_move_line_ids.product_uom_id')
|
||||
def _compute_done_quantity(self):
|
||||
super(StockMove, self)._compute_done_quantity()
|
||||
|
||||
@@ -196,18 +196,19 @@
|
||||
<label for="duration"/>
|
||||
<div>
|
||||
<button style="pointer-events: none;" class="oe_inline label label-default">
|
||||
<field name="duration" widget="mrp_time_counter"/>
|
||||
<field name="duration" widget="mrp_time_counter" help="Time the currently logged user spent on this workorder."/>
|
||||
</button>
|
||||
</div>
|
||||
</group>
|
||||
</group>
|
||||
<group>
|
||||
<field name="time_ids" nolabel="1">
|
||||
<field name="time_ids" nolabel="1" context="{'default_workcenter_id': workcenter_id}">
|
||||
<tree>
|
||||
<field name="date_start"/>
|
||||
<field name="date_end"/>
|
||||
<field name="duration" widget="float_time" sum="Total duration"/>
|
||||
<field name="user_id"/>
|
||||
<field name="workcenter_id" invisible="1"/>
|
||||
<field name="loss_id" string="Efficiency"/>
|
||||
</tree>
|
||||
<form>
|
||||
@@ -219,6 +220,7 @@
|
||||
</group>
|
||||
<group>
|
||||
<field name="user_id"/>
|
||||
<field name="workcenter_id"/>
|
||||
<field name="loss_id"/>
|
||||
</group>
|
||||
</group>
|
||||
|
||||
@@ -59,6 +59,15 @@ class PaymentAcquirerAuthorize(models.Model):
|
||||
@api.multi
|
||||
def authorize_form_generate_values(self, values):
|
||||
self.ensure_one()
|
||||
# State code is only supported in US, use state name by default
|
||||
# See https://developer.authorize.net/api/reference/
|
||||
state = values['partner_state'].name if values.get('partner_state') else ''
|
||||
if values.get('partner_country') and values.get('partner_country') == self.env.ref('base.us', False):
|
||||
state = values['partner_state'].code if values.get('partner_state') else ''
|
||||
billing_state = values['billing_partner_state'].name if values.get('billing_partner_state') else ''
|
||||
if values.get('billing_partner_country') and values.get('billing_partner_country') == self.env.ref('base.us', False):
|
||||
billing_state = values['billing_partner_state'].code if values.get('billing_partner_state') else ''
|
||||
|
||||
base_url = self.env['ir.config_parameter'].get_param('web.base.url')
|
||||
authorize_tx_values = dict(values)
|
||||
temp_authorize_tx_values = {
|
||||
@@ -83,7 +92,7 @@ class PaymentAcquirerAuthorize(models.Model):
|
||||
'first_name': values.get('partner_first_name'),
|
||||
'last_name': values.get('partner_last_name'),
|
||||
'phone': values.get('partner_phone'),
|
||||
'state': values.get('partner_state') and values['partner_state'].code or '',
|
||||
'state': state,
|
||||
'billing_address': values.get('billing_partner_address'),
|
||||
'billing_city': values.get('billing_partner_city'),
|
||||
'billing_country': values.get('billing_partner_country') and values.get('billing_partner_country').name or '',
|
||||
@@ -92,7 +101,7 @@ class PaymentAcquirerAuthorize(models.Model):
|
||||
'billing_first_name': values.get('billing_partner_first_name'),
|
||||
'billing_last_name': values.get('billing_partner_last_name'),
|
||||
'billing_phone': values.get('billing_partner_phone'),
|
||||
'billing_state': values.get('billing_partner_state') and values['billing_partner_state'].code or '',
|
||||
'billing_state': billing_state,
|
||||
}
|
||||
temp_authorize_tx_values['returndata'] = authorize_tx_values.pop('return_url', '')
|
||||
temp_authorize_tx_values['x_fp_hash'] = self._authorize_generate_hashing(temp_authorize_tx_values)
|
||||
|
||||
@@ -110,7 +110,7 @@ class OgoneController(http.Controller):
|
||||
def feedback(self, **kwargs):
|
||||
try:
|
||||
tx = request.env['payment.transaction'].sudo()._ogone_form_get_tx_from_data(kwargs)
|
||||
tx._ogone_s2s_validate()
|
||||
tx._ogone_s2s_validate_tree(kwargs)
|
||||
except ValidationError:
|
||||
return 'ko'
|
||||
return 'ok'
|
||||
|
||||
@@ -87,74 +87,30 @@ odoo.define('payment_stripe.stripe', function(require) {
|
||||
payment_form.append('<i class="fa fa-spinner fa-spin"/>');
|
||||
payment_form.attr('disabled','disabled');
|
||||
|
||||
var acquirer_id = payment_form.find('input[type="radio"][data-provider="stripe"]:checked').data('acquirer-id');
|
||||
if (! acquirer_id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var payment_tx_url = payment_form.find('input[name="prepare_tx_url"]').val();
|
||||
var access_token = $("input[name='access_token']").val() || $("input[name='token']").val() || '';
|
||||
var so_id = $("input[name='return_url']").val().match(/quote\/([0-9]+)/) || undefined;
|
||||
if (so_id) {
|
||||
so_id = parseInt(so_id[1]);
|
||||
}
|
||||
var invoice_id = $("input[name='return_url']").val().match(/invoices\/([0-9]+)/) || undefined;
|
||||
if (invoice_id) {
|
||||
invoice_id = parseInt(invoice_id[1]);
|
||||
|
||||
var get_input_value = function(name) {
|
||||
return provider_form.find('input[name="' + name + '"]').val();
|
||||
}
|
||||
|
||||
var currency = $("input[name='currency']").val();
|
||||
var currency_id = $("input[name='currency_id']").val();
|
||||
var amount = parseFloat($("input[name='amount']").val() || '0.0');
|
||||
var acquirer_id = parseInt(provider_form.find('#acquirer_stripe').val());
|
||||
var amount = parseFloat(get_input_value("amount") || '0.0');
|
||||
var currency = get_input_value("currency");
|
||||
var email = get_input_value("email");
|
||||
var invoice_num = get_input_value("invoice_num");
|
||||
var merchant = get_input_value("merchant");
|
||||
|
||||
|
||||
if ($('.o_website_payment').length !== 0) {
|
||||
var invoice_num = $("input[name='invoice_num']").val();
|
||||
var url = _.str.sprintf("/website_payment/transaction/v2/%f/%s/%s",
|
||||
amount, currency_id, invoice_num);
|
||||
|
||||
var create_tx = ajax.jsonRpc(url, 'call', {
|
||||
acquirer_id: acquirer_id
|
||||
}).then(function (data) {
|
||||
try { provider_form[0].innerHTML = data; } catch (e) {}
|
||||
});
|
||||
}
|
||||
else if ($('.o_website_quote').length !== 0) {
|
||||
var url = _.str.sprintf("/quote/%s/transaction/", so_id);
|
||||
var create_tx = ajax.jsonRpc(url, 'call', {
|
||||
access_token: access_token,
|
||||
acquirer_id: acquirer_id
|
||||
}).then(function (data) {
|
||||
try { provider_form[0].innerHTML = data; } catch (e) {};
|
||||
});
|
||||
} else if (window.location.href.includes("/my/orders/")) {
|
||||
var create_tx = ajax.jsonRpc('/pay/sale/' + so_id + '/form_tx/', 'call', {
|
||||
access_token: access_token,
|
||||
acquirer_id: acquirer_id
|
||||
}).then(function (data) {
|
||||
try { provider_form.innerHTML = data; } catch (e) {};
|
||||
});
|
||||
} else if (window.location.href.includes("/my/invoices/")) {
|
||||
var create_tx = ajax.jsonRpc('/invoice/pay/' + invoice_id + '/form_tx/', 'call', {
|
||||
access_token: access_token,
|
||||
acquirer_id: acquirer_id
|
||||
}).then(function (data) {
|
||||
try { provider_form.innerHTML = data; } catch (e) {};
|
||||
});
|
||||
}
|
||||
else {
|
||||
var create_tx = ajax.jsonRpc('/shop/payment/transaction/' + acquirer_id, 'call', {
|
||||
so_id: so_id,
|
||||
access_token: access_token,
|
||||
acquirer_id: acquirer_id
|
||||
}).then(function (data) {
|
||||
try { provider_form.innerHTML = data; } catch (e) {};
|
||||
});
|
||||
}
|
||||
create_tx.done(function () {
|
||||
ajax.jsonRpc(payment_tx_url, 'call', {
|
||||
acquirer_id: acquirer_id,
|
||||
access_token: access_token,
|
||||
}).then(function(data) {
|
||||
try { provider_form[0].innerHTML = data; } catch (e) {};
|
||||
}).done(function () {
|
||||
getStripeHandler().open({
|
||||
name: $("input[name='merchant']").val(),
|
||||
description: $("input[name='invoice_num']").val(),
|
||||
email: $("input[name='email']").val(),
|
||||
name: merchant,
|
||||
description: invoice_num,
|
||||
email: email,
|
||||
currency: currency,
|
||||
amount: _.contains(int_currencies, currency) ? amount : amount * 100,
|
||||
});
|
||||
|
||||
@@ -95,4 +95,4 @@ msgstr ""
|
||||
#. module: phone_validation
|
||||
#: model:ir.model,name:phone_validation.model_phone_validation_mixin
|
||||
msgid "phone.validation.mixin"
|
||||
msgstr ""
|
||||
msgstr "phone.validation.mixin"
|
||||
|
||||
@@ -4132,6 +4132,9 @@ msgid ""
|
||||
"You cannot confirm all orders of this session, because they have not the 'paid' status.\n"
|
||||
"{reference} is in state {state}, total amount: {total}, paid: {paid}"
|
||||
msgstr ""
|
||||
"Bu oturumun tüm siparişlerini onaylayamazsınız, çünkü \"ödendi\" durumları "
|
||||
"yoktur. {Reference} durum {state} durumundadır, toplam tutar: {total}, "
|
||||
"ödendi: {paid}"
|
||||
|
||||
#. module: point_of_sale
|
||||
#: code:addons/point_of_sale/models/pos_session.py:151
|
||||
|
||||
@@ -38,8 +38,8 @@ class PosSession(models.Model):
|
||||
paid=order.amount_paid,
|
||||
))
|
||||
order.action_pos_order_done()
|
||||
orders = session.order_ids.filtered(lambda order: order.state in ['invoiced', 'done'])
|
||||
orders.sudo()._reconcile_payments()
|
||||
orders_to_reconcile = session.order_ids.filtered(lambda order: order.state in ['invoiced', 'done'] and order.partner_id)
|
||||
orders_to_reconcile.sudo()._reconcile_payments()
|
||||
|
||||
config_id = fields.Many2one(
|
||||
'pos.config', string='Point of Sale',
|
||||
|
||||
@@ -27,6 +27,18 @@ class ProductTemplate(models.Model):
|
||||
self.available_in_pos = False
|
||||
|
||||
|
||||
class ProductProduct(models.Model):
|
||||
_inherit = 'product.product'
|
||||
|
||||
@api.multi
|
||||
def unlink(self):
|
||||
product_ctx = dict(self.env.context or {}, active_test=False)
|
||||
if self.env['pos.session'].search_count([('state', '!=', 'closed')]):
|
||||
if self.with_context(product_ctx).search_count([('id', 'in', self.ids), ('product_tmpl_id.available_in_pos', '=', True)]):
|
||||
raise UserError(_('You cannot delete a product saleable in point of sale while a session is still opened.'))
|
||||
return super(ProductProduct, self).unlink()
|
||||
|
||||
|
||||
class UomCateg(models.Model):
|
||||
_inherit = 'uom.category'
|
||||
|
||||
|
||||
@@ -1406,6 +1406,7 @@ exports.Orderline = Backbone.Model.extend({
|
||||
this.set_unit_price(this.product.get_price(this.order.pricelist, this.get_quantity()));
|
||||
this.order.fix_tax_included_price(this);
|
||||
}
|
||||
this.trigger('change', this);
|
||||
},
|
||||
// return the quantity of product
|
||||
get_quantity: function(){
|
||||
@@ -2124,7 +2125,7 @@ exports.Order = Backbone.Model.extend({
|
||||
company_registry: company.company_registry,
|
||||
contact_address: company.partner_id[1],
|
||||
vat: company.vat,
|
||||
vat_label: company.country.vat_label,
|
||||
vat_label: company.country && company.country.vat_label || '',
|
||||
name: company.name,
|
||||
phone: company.phone,
|
||||
logo: this.pos.company_logo_base64,
|
||||
|
||||
@@ -100,7 +100,7 @@ class PortalChatter(http.Controller):
|
||||
raise Forbidden()
|
||||
# Non-employee see only messages with not internal subtype (aka, no internal logs)
|
||||
if not request.env['res.users'].has_group('base.group_user'):
|
||||
domain = expression.AND([['&', '&', ('subtype_id', '!=', False), ('subtype_id.internal', '=', False)], domain])
|
||||
domain = expression.AND([['&', ('subtype_id', '!=', False), ('subtype_id.internal', '=', False)], domain])
|
||||
Message = request.env['mail.message'].sudo()
|
||||
return {
|
||||
'messages': Message.search(domain, limit=limit, offset=offset).portal_message_format(),
|
||||
|
||||
@@ -626,7 +626,7 @@ msgstr "oturum açıldı"
|
||||
#. module: portal
|
||||
#: model:ir.model,name:portal.model_portal_mixin
|
||||
msgid "portal.mixin"
|
||||
msgstr ""
|
||||
msgstr "portal.mixin"
|
||||
|
||||
#. module: portal
|
||||
#: model:ir.ui.view,arch_db:portal.portal_my_details
|
||||
|
||||
@@ -134,7 +134,7 @@ class PortalWizardUser(models.TransientModel):
|
||||
if wizard_user.partner_id.company_id:
|
||||
company_id = wizard_user.partner_id.company_id.id
|
||||
else:
|
||||
company_id = self.env['res.company']._company_default_get('res.users')
|
||||
company_id = self.env['res.company']._company_default_get('res.users').id
|
||||
user_portal = wizard_user.sudo().with_context(company_id=company_id)._create_user()
|
||||
else:
|
||||
user_portal = user
|
||||
|
||||
@@ -110,7 +110,7 @@ msgstr "Ürün Alanı"
|
||||
#. module: pos_cache
|
||||
#: model:ir.model,name:pos_cache.model_pos_cache
|
||||
msgid "pos.cache"
|
||||
msgstr ""
|
||||
msgstr "pos.cache"
|
||||
|
||||
#. module: pos_cache
|
||||
#: model:ir.model,name:pos_cache.model_pos_config
|
||||
|
||||
@@ -752,14 +752,14 @@ msgstr "pos.config"
|
||||
#. module: pos_restaurant
|
||||
#: model:ir.model,name:pos_restaurant.model_restaurant_floor
|
||||
msgid "restaurant.floor"
|
||||
msgstr ""
|
||||
msgstr "restaurant.floor"
|
||||
|
||||
#. module: pos_restaurant
|
||||
#: model:ir.model,name:pos_restaurant.model_restaurant_printer
|
||||
msgid "restaurant.printer"
|
||||
msgstr ""
|
||||
msgstr "restaurant.printer"
|
||||
|
||||
#. module: pos_restaurant
|
||||
#: model:ir.model,name:pos_restaurant.model_restaurant_table
|
||||
msgid "restaurant.table"
|
||||
msgstr ""
|
||||
msgstr "restaurant.table"
|
||||
|
||||
@@ -220,6 +220,7 @@ class ProductProduct(models.Model):
|
||||
for supplier_info in self.seller_ids:
|
||||
if supplier_info.name.id == self._context.get('partner_id'):
|
||||
self.code = supplier_info.product_code or self.default_code
|
||||
break
|
||||
else:
|
||||
self.code = self.default_code
|
||||
|
||||
@@ -229,6 +230,7 @@ class ProductProduct(models.Model):
|
||||
if supplier_info.name.id == self._context.get('partner_id'):
|
||||
product_name = supplier_info.product_name or self.default_code
|
||||
self.partner_ref = '%s%s' % (self.code and '[%s] ' % self.code or '', product_name)
|
||||
break
|
||||
else:
|
||||
self.partner_ref = self.name_get()[0][1]
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
from odoo import api, fields, models, _
|
||||
from odoo.addons import decimal_precision as dp
|
||||
from odoo.exceptions import UserError, ValidationError
|
||||
from odoo.osv import expression
|
||||
|
||||
|
||||
class ProductAttribute(models.Model):
|
||||
@@ -107,6 +108,6 @@ class ProductAttributeLine(models.Model):
|
||||
# search on a m2o and one on a m2m, probably this will quickly become
|
||||
# difficult to compute - check if performance optimization is required
|
||||
if name and operator in ('=', 'ilike', '=ilike', 'like', '=like'):
|
||||
args = ['|', ('attribute_id', operator, name), ('value_ids', operator, name)]
|
||||
args = expression.AND([['|', ('attribute_id', operator, name), ('value_ids', operator, name)], args])
|
||||
return self.search(args, limit=limit).name_get()
|
||||
return super(ProductAttributeLine, self).name_search(name=name, args=args, operator=operator, limit=limit)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import test_variants, test_pricelist, test_product_pricelist
|
||||
from . import test_variants, test_pricelist, test_product_pricelist, test_seller
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo.tests.common import TransactionCase
|
||||
|
||||
|
||||
class TestSeller(TransactionCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestSeller, self).setUp()
|
||||
self.product_service = self.env.ref('product.product_product_2')
|
||||
self.product_service.default_code = 'DEFCODE'
|
||||
self.asustec = self.env.ref('base.res_partner_1')
|
||||
self.camptocamp = self.env.ref('base.res_partner_12')
|
||||
|
||||
def test_10_sellers(self):
|
||||
self.product_service.write({'seller_ids': [
|
||||
(0, 0, {'name': self.asustec.id, 'product_code': 'ASUCODE'}),
|
||||
(0, 0, {'name': self.camptocamp.id, 'product_code': 'C2CCODE'}),
|
||||
]})
|
||||
|
||||
default_code = self.product_service.code
|
||||
self.assertEqual("DEFCODE", default_code, "Default code not used in product name")
|
||||
|
||||
context_code = self.product_service\
|
||||
.with_context(partner_id=self.camptocamp.id)\
|
||||
.code
|
||||
self.assertEqual('C2CCODE', context_code, "Partner's code not used in product name with context set")
|
||||
@@ -107,6 +107,8 @@ msgid ""
|
||||
"Number of days before the goods should be removed from the stock. It will be"
|
||||
" computed on the lot/serial number."
|
||||
msgstr ""
|
||||
"Malların stoktan çıkarılmadan önce gereken gün sayısı. Bu lot / seri "
|
||||
"numarası üzerinde hesaplanacaktır.."
|
||||
|
||||
#. module: product_expiry
|
||||
#: model:ir.model.fields,help:product_expiry.field_product_product__use_time
|
||||
|
||||
@@ -143,7 +143,7 @@ class CustomerPortal(CustomerPortal):
|
||||
# pager
|
||||
pager = portal_pager(
|
||||
url="/my/tasks",
|
||||
url_args={'date_begin': date_begin, 'date_end': date_end, 'sortby': sortby, 'filterby': filterby},
|
||||
url_args={'date_begin': date_begin, 'date_end': date_end, 'sortby': sortby, 'filterby': filterby, 'search_in': search_in, 'search': search},
|
||||
total=task_count,
|
||||
page=page,
|
||||
step=self._items_per_page
|
||||
|
||||
@@ -1523,6 +1523,8 @@ msgid ""
|
||||
"Lets the company customize which Pad installation should be used to link to "
|
||||
"new pads (for example: http://etherpad.com/)."
|
||||
msgstr ""
|
||||
"Şirketin yeni pedlere bağlantı kurmak için hangi Pad kurulumunun "
|
||||
"kullanılması gerektiğini özelleştirir (örneğin: http://etherpad.com/)."
|
||||
|
||||
#. module: project
|
||||
#: model:ir.model.fields,help:project.field_project_project__analytic_account_id
|
||||
|
||||
@@ -868,7 +868,7 @@ class Task(models.Model):
|
||||
groups = [new_group] + groups
|
||||
|
||||
for group_name, group_method, group_data in groups:
|
||||
if group_name in ('customer'):
|
||||
if group_name == 'customer':
|
||||
continue
|
||||
group_data['has_button_access'] = True
|
||||
|
||||
|
||||
@@ -537,7 +537,7 @@ class PurchaseOrder(models.Model):
|
||||
result['context'] = {}
|
||||
pick_ids = self.mapped('picking_ids')
|
||||
#choose the view_mode accordingly
|
||||
if len(pick_ids) > 1:
|
||||
if not pick_ids or len(pick_ids) > 1:
|
||||
result['domain'] = "[('id','in',%s)]" % (pick_ids.ids)
|
||||
elif len(pick_ids) == 1:
|
||||
res = self.env.ref('stock.view_picking_form', False)
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<div>
|
||||
<strong>The ordered quantity has been updated.</strong>
|
||||
<ul>
|
||||
<li><t t-esc="line.order_id.product_id.display_name"/>:</li>
|
||||
<li><t t-esc="line.product_id.display_name"/>:</li>
|
||||
Ordered Quantity: <t t-esc="line.product_qty" /> -> <t t-esc="float(product_qty)"/><br/>
|
||||
<t t-if='line.order_id.product_id.type in ("consu", "product")'>
|
||||
Received Quantity: <t t-esc="line.qty_received" /><br/>
|
||||
|
||||
@@ -990,4 +990,4 @@ msgstr "stock.traceability.report"
|
||||
#. module: repair
|
||||
#: model:ir.model,name:repair.model_stock_warn_insufficient_qty_repair
|
||||
msgid "stock.warn.insufficient.qty.repair"
|
||||
msgstr ""
|
||||
msgstr "stock.warn.insufficient.qty.repair"
|
||||
|
||||
@@ -200,9 +200,11 @@ class Repair(models.Model):
|
||||
def action_validate(self):
|
||||
self.ensure_one()
|
||||
precision = self.env['decimal.precision'].precision_get('Product Unit of Measure')
|
||||
available_qty = self.env['stock.quant']._get_available_quantity(self.product_id, self.location_id, self.lot_id, strict=True)
|
||||
if float_compare(available_qty, self.product_qty, precision_digits=precision) >= 0:
|
||||
return self.action_repair_confirm()
|
||||
available_qty_owner = self.env['stock.quant']._get_available_quantity(self.product_id, self.location_id, self.lot_id, owner_id=self.partner_id, strict=True)
|
||||
available_qty_noown = self.env['stock.quant']._get_available_quantity(self.product_id, self.location_id, self.lot_id, strict=True)
|
||||
for available_qty in [available_qty_owner, available_qty_noown]:
|
||||
if float_compare(available_qty, self.product_qty, precision_digits=precision) >= 0:
|
||||
return self.action_repair_confirm()
|
||||
else:
|
||||
return {
|
||||
'name': _('Insufficient Quantity'),
|
||||
@@ -428,8 +430,15 @@ class Repair(models.Model):
|
||||
if self.filtered(lambda repair: not repair.repaired):
|
||||
raise UserError(_("Repair must be repaired in order to make the product moves."))
|
||||
res = {}
|
||||
precision = self.env['decimal.precision'].precision_get('Product Unit of Measure')
|
||||
Move = self.env['stock.move']
|
||||
for repair in self:
|
||||
# Try to create move with the appropriate owner
|
||||
owner_id = False
|
||||
available_qty_owner = self.env['stock.quant']._get_available_quantity(repair.product_id, repair.location_id, repair.lot_id, owner_id=repair.partner_id, strict=True)
|
||||
if float_compare(available_qty_owner, repair.product_qty, precision_digits=precision) >= 0:
|
||||
owner_id = repair.partner_id.id
|
||||
|
||||
moves = self.env['stock.move']
|
||||
for operation in repair.operations:
|
||||
move = Move.create({
|
||||
@@ -447,6 +456,7 @@ class Repair(models.Model):
|
||||
'qty_done': operation.product_uom_qty,
|
||||
'package_id': False,
|
||||
'result_package_id': False,
|
||||
'owner_id': owner_id,
|
||||
'location_id': operation.location_id.id, #TODO: owner stuff
|
||||
'location_dest_id': operation.location_dest_id.id,})],
|
||||
'repair_id': repair.id,
|
||||
@@ -469,6 +479,7 @@ class Repair(models.Model):
|
||||
'qty_done': repair.product_qty,
|
||||
'package_id': False,
|
||||
'result_package_id': False,
|
||||
'owner_id': owner_id,
|
||||
'location_id': repair.location_id.id, #TODO: owner stuff
|
||||
'location_dest_id': repair.location_id.id,})],
|
||||
'repair_id': repair.id,
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<odoo>
|
||||
<data>
|
||||
<template id="report_repairorder">
|
||||
<t t-foreach="docs" t-as="o">
|
||||
<t t-set="o" t-value="doc"/>
|
||||
<t t-call="web.external_layout">
|
||||
<t t-set="o" t-value="o.with_context({'lang': o.partner_id.lang})" />
|
||||
<div class="page">
|
||||
@@ -155,7 +155,6 @@
|
||||
<div class="oe_structure"/>
|
||||
</div>
|
||||
</t>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<template id="report_repairorder2">
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<field name="name">report.all.channels.sales.pivot</field>
|
||||
<field name="model">report.all.channels.sales</field>
|
||||
<field name="arch" type="xml">
|
||||
<pivot string="Sales by Channel Analysis">
|
||||
<pivot string="Sales by Channel Analysis" disable_linking="True">
|
||||
<field name="name" type="row"/>
|
||||
<field name="price_total" string="Total Price" type="measure"/>
|
||||
</pivot>
|
||||
|
||||
@@ -176,7 +176,7 @@ msgstr "Ödeme İşlemi"
|
||||
#. module: sale_payment
|
||||
#: model:ir.ui.view,arch_db:sale_payment.crm_team_salesteams_view_kanban_inherit_website_portal_sale
|
||||
msgid "Payment to Capture"
|
||||
msgstr ""
|
||||
msgstr "Yakalanan Ödeme"
|
||||
|
||||
#. module: sale_payment
|
||||
#: model:ir.ui.view,arch_db:sale_payment.crm_team_salesteams_view_kanban_inherit_website_portal_sale
|
||||
|
||||
@@ -1,7 +1,18 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import api, SUPERUSER_ID
|
||||
|
||||
from . import models
|
||||
from . import wizard
|
||||
from . import controllers
|
||||
from . import report
|
||||
|
||||
def uninstall_hook(cr, registry):
|
||||
env = api.Environment(cr, SUPERUSER_ID, {})
|
||||
env['product.template'].search([
|
||||
('service_type', '=', 'timesheet')
|
||||
]).write({'service_type': 'manual'})
|
||||
env['product.product'].search([
|
||||
('service_type', '=', 'timesheet')
|
||||
]).write({'service_type': 'manual'})
|
||||
|
||||
@@ -33,4 +33,5 @@ have real delivered quantities in sales orders.
|
||||
'data/sale_service_demo.xml',
|
||||
],
|
||||
'auto_install': True,
|
||||
'uninstall_hook': 'uninstall_hook',
|
||||
}
|
||||
|
||||
@@ -6851,7 +6851,7 @@ msgstr "stock.fixed.putaway.strat"
|
||||
#. module: stock
|
||||
#: model:ir.model,name:stock.model_stock_overprocessed_transfer
|
||||
msgid "stock.overprocessed.transfer"
|
||||
msgstr ""
|
||||
msgstr "stock.overprocessed.transfer"
|
||||
|
||||
#. module: stock
|
||||
#: model:ir.model,name:stock.model_stock_return_picking_line
|
||||
@@ -6871,12 +6871,12 @@ msgstr "stock.traceability.report"
|
||||
#. module: stock
|
||||
#: model:ir.model,name:stock.model_stock_warn_insufficient_qty
|
||||
msgid "stock.warn.insufficient.qty"
|
||||
msgstr ""
|
||||
msgstr "stock.warn.insufficient.qty"
|
||||
|
||||
#. module: stock
|
||||
#: model:ir.model,name:stock.model_stock_warn_insufficient_qty_scrap
|
||||
msgid "stock.warn.insufficient.qty.scrap"
|
||||
msgstr ""
|
||||
msgstr "stock.warn.insufficient.qty.scrap"
|
||||
|
||||
#. module: stock
|
||||
#: model:ir.ui.view,arch_db:stock.view_inventory_form
|
||||
|
||||
@@ -98,13 +98,13 @@ class Product(models.Model):
|
||||
|
||||
domain_move_in = [('product_id', 'in', self.ids)] + domain_move_in_loc
|
||||
domain_move_out = [('product_id', 'in', self.ids)] + domain_move_out_loc
|
||||
if lot_id:
|
||||
if lot_id is not None:
|
||||
domain_quant += [('lot_id', '=', lot_id)]
|
||||
if owner_id:
|
||||
if owner_id is not None:
|
||||
domain_quant += [('owner_id', '=', owner_id)]
|
||||
domain_move_in += [('restrict_partner_id', '=', owner_id)]
|
||||
domain_move_out += [('restrict_partner_id', '=', owner_id)]
|
||||
if package_id:
|
||||
if package_id is not None:
|
||||
domain_quant += [('package_id', '=', package_id)]
|
||||
if dates_in_the_past:
|
||||
domain_move_in_done = list(domain_move_in)
|
||||
|
||||
@@ -661,7 +661,7 @@ class StockMove(models.Model):
|
||||
self.product_uom = product.uom_id.id
|
||||
return {'domain': {'product_uom': [('category_id', '=', product.uom_id.category_id.id)]}}
|
||||
|
||||
@api.onchange('date')
|
||||
@api.onchange('date_expected')
|
||||
def onchange_date(self):
|
||||
if self.date_expected:
|
||||
self.date = self.date_expected
|
||||
@@ -877,11 +877,6 @@ class StockMove(models.Model):
|
||||
package_id=package_id, owner_id=owner_id, strict=strict
|
||||
)
|
||||
except UserError:
|
||||
# If it raises here, it means that the `available_quantity` brought by a done move line
|
||||
# is not available on the quants itself. This could be the result of an inventory
|
||||
# adjustment that removed totally of partially `available_quantity`. When this happens, we
|
||||
# chose to do nothing. This situation could not happen on MTS move, because in this case
|
||||
# `available_quantity` is directly the quantity on the quants themselves.
|
||||
taken_quantity = 0
|
||||
|
||||
# Find a candidate move line to update or create a new one.
|
||||
@@ -997,7 +992,17 @@ class StockMove(models.Model):
|
||||
available_move_lines[(move_line.location_id, move_line.lot_id, move_line.result_package_id, move_line.owner_id)] -= move_line.product_qty
|
||||
for (location_id, lot_id, package_id, owner_id), quantity in available_move_lines.items():
|
||||
need = move.product_qty - sum(move.move_line_ids.mapped('product_qty'))
|
||||
taken_quantity = move._update_reserved_quantity(need, quantity, location_id, lot_id, package_id, owner_id)
|
||||
# `quantity` is what is brought by chained done move lines. We double check
|
||||
# here this quantity is available on the quants themselves. If not, this
|
||||
# could be the result of an inventory adjustment that removed totally of
|
||||
# partially `quantity`. When this happens, we chose to reserve the maximum
|
||||
# still available. This situation could not happen on MTS move, because in
|
||||
# this case `quantity` is directly the quantity on the quants themselves.
|
||||
available_quantity = self.env['stock.quant']._get_available_quantity(
|
||||
move.product_id, location_id, lot_id=lot_id, package_id=package_id, owner_id=owner_id, strict=True)
|
||||
if float_is_zero(available_quantity, precision_rounding=move.product_id.uom_id.rounding):
|
||||
continue
|
||||
taken_quantity = move._update_reserved_quantity(need, min(quantity, available_quantity), location_id, lot_id, package_id, owner_id)
|
||||
if float_is_zero(taken_quantity, precision_rounding=move.product_id.uom_id.rounding):
|
||||
continue
|
||||
if need - taken_quantity == 0.0:
|
||||
@@ -1079,6 +1084,9 @@ class StockMove(models.Model):
|
||||
break
|
||||
return extra_move
|
||||
|
||||
def _unreserve_initial_demand(self, new_move):
|
||||
pass
|
||||
|
||||
def _action_done(self):
|
||||
self.filtered(lambda move: move.state == 'draft')._action_confirm() # MRP allows scrapping draft moves
|
||||
|
||||
@@ -1119,9 +1127,7 @@ class StockMove(models.Model):
|
||||
move_line.write({'product_uom_qty': move_line.qty_done})
|
||||
except UserError:
|
||||
pass
|
||||
|
||||
# If you were already putting stock.move.lots on the next one in the work order, transfer those to the new move
|
||||
move.move_line_ids.filtered(lambda x: x.qty_done == 0.0).write({'move_id': new_move, 'product_uom_qty': 0})
|
||||
move._unreserve_initial_demand(new_move)
|
||||
move.move_line_ids._action_done()
|
||||
# Check the consistency of the result packages; there should be an unique location across
|
||||
# the contained quants.
|
||||
|
||||
@@ -99,6 +99,7 @@
|
||||
<span t-if="o.state != 'done'" t-field="ml.product_uom_qty"/>
|
||||
<span t-if="o.state == 'done'" t-field="ml.qty_done"/>
|
||||
<span t-field="ml.product_uom_id" groups="uom.group_uom"/>
|
||||
|
||||
</td>
|
||||
<td width="20%" t-if="o.picking_type_id.code != 'incoming'">
|
||||
<span t-esc="ml.location_id.display_name"/>
|
||||
@@ -116,11 +117,15 @@
|
||||
</td>
|
||||
<td width="15%" class=" text-center h6" t-if="has_serial_number">
|
||||
<img t-if="has_serial_number and (ml.lot_id or ml.lot_name)" t-att-src="'/report/barcode/?type=%s&value=%s&width=%s&height=%s&humanreadable=1' % ('Code128', ml.lot_id.name, 600, 100)" style="width:100%;height:35px;"/>
|
||||
|
||||
</td>
|
||||
<td width="15%" class="text-center" t-if="has_barcode">
|
||||
<t t-if="product_barcode != move.product_id.barcode">
|
||||
<span t-if="move.product_id and move.product_id.barcode">
|
||||
<img t-att-src="'/report/barcode/?type=%s&value=%s&width=%s&height=%s' % ('Code128', move.product_id.barcode, 600, 100)" style="width:100%;height:35px"/>
|
||||
<img t-if="len(move.product_id.barcode) == 13" t-att-src="'/report/barcode/?type=%s&value=%s&width=%s&height=%s' % ('EAN13', move.product_id.barcode, 600, 100)" style="width:100%;height:35px"/>
|
||||
<img t-elif="len(move.product_id.barcode) == 8" t-att-src="'/report/barcode/?type=%s&value=%s&width=%s&height=%s' % ('EAN8', move.product_id.barcode, 600, 100)" style="width:100%;height:35px"/>
|
||||
<img t-else="" t-att-src="'/report/barcode/?type=%s&value=%s&width=%s&height=%s' % ('Code128', move.product_id.barcode, 600, 100)" style="width:100%;height:35px"/>
|
||||
|
||||
</span>
|
||||
<t t-set="product_barcode" t-value="move.product_id.barcode"/>
|
||||
</t>
|
||||
|
||||
@@ -1901,18 +1901,29 @@ class StockMove(TransactionCase):
|
||||
ml.qty_done = 1
|
||||
res_dict_for_back_order = picking_pack_cust.button_validate()
|
||||
backorder_wizard = self.env[(res_dict_for_back_order.get('res_model'))].browse(res_dict_for_back_order.get('res_id'))
|
||||
backorder_wizard.with_context(debug=True).process()
|
||||
backorder_wizard.process()
|
||||
backorder = self.env['stock.picking'].search([('backorder_id', '=', picking_pack_cust.id)])
|
||||
backordered_ml = backorder.move_line_ids
|
||||
quant_lot3 = self.env['stock.quant'].search([('lot_id', '=', backordered_ml.lot_id.id)])
|
||||
backordered_move = backorder.move_lines
|
||||
|
||||
# due to the rounding, the backordered quantity is 0.999 ; we shoudln't be able to reserve
|
||||
# 0.999 on a tracked by serial number quant
|
||||
self.assertEqual(backordered_ml.product_uom_qty, 0)
|
||||
self.assertTrue(backordered_ml.lot_id)
|
||||
backordered_move._action_assign()
|
||||
self.assertEqual(backordered_move.reserved_availability, 0)
|
||||
|
||||
# force the serial number and validate
|
||||
backordered_ml.qty_done = 1
|
||||
lot3 = self.env['stock.production.lot'].search([('name', '=', "lot3")])
|
||||
backorder.write({'move_line_ids': [(0, 0, {
|
||||
'product_id': self.product2.id,
|
||||
'product_uom_id': self.uom_unit.id,
|
||||
'qty_done': 1,
|
||||
'product_uom_qty': 0,
|
||||
'lot_id': lot3.id,
|
||||
'package_id': False,
|
||||
'result_package_id': False,
|
||||
'location_id': backordered_move.location_id.id,
|
||||
'location_dest_id': backordered_move.location_dest_id.id,
|
||||
'move_id': backordered_move.id,
|
||||
})]})
|
||||
|
||||
overprocessed_wizard = backorder.button_validate()
|
||||
overprocessed_wizard = self.env['stock.overprocessed.transfer'].browse(overprocessed_wizard['res_id'])
|
||||
@@ -1921,6 +1932,57 @@ class StockMove(TransactionCase):
|
||||
self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product2, self.customer_location), 3)
|
||||
self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product2, self.pack_location), 0)
|
||||
|
||||
def test_link_assign_10(self):
|
||||
""" Test the assignment mechanism with partial availability.
|
||||
"""
|
||||
# make some stock:
|
||||
# stock location: 2.0
|
||||
# pack location: -1.0
|
||||
self.env['stock.quant']._update_available_quantity(self.product1, self.stock_location, 2.0)
|
||||
self.assertEqual(len(self.env['stock.quant']._gather(self.product1, self.stock_location)), 1.0)
|
||||
|
||||
move_out = self.env['stock.move'].create({
|
||||
'name': 'test_link_assign_out',
|
||||
'location_id': self.pack_location.id,
|
||||
'location_dest_id': self.customer_location.id,
|
||||
'product_id': self.product1.id,
|
||||
'product_uom': self.uom_unit.id,
|
||||
'product_uom_qty': 1.0,
|
||||
})
|
||||
move_out._action_confirm()
|
||||
move_out._action_assign()
|
||||
move_out.quantity_done = 1.0
|
||||
move_out._action_done()
|
||||
self.assertEqual(len(self.env['stock.quant']._gather(self.product1, self.pack_location)), 1.0)
|
||||
|
||||
move_stock_pack = self.env['stock.move'].create({
|
||||
'name': 'test_link_assign_1_1',
|
||||
'location_id': self.stock_location.id,
|
||||
'location_dest_id': self.pack_location.id,
|
||||
'product_id': self.product1.id,
|
||||
'product_uom': self.uom_unit.id,
|
||||
'product_uom_qty': 2.0,
|
||||
})
|
||||
move_pack_cust = self.env['stock.move'].create({
|
||||
'name': 'test_link_assign_1_2',
|
||||
'location_id': self.pack_location.id,
|
||||
'location_dest_id': self.customer_location.id,
|
||||
'product_id': self.product1.id,
|
||||
'product_uom': self.uom_unit.id,
|
||||
'product_uom_qty': 2.0,
|
||||
})
|
||||
move_stock_pack.write({'move_dest_ids': [(4, move_pack_cust.id, 0)]})
|
||||
move_pack_cust.write({'move_orig_ids': [(4, move_stock_pack.id, 0)]})
|
||||
|
||||
(move_stock_pack + move_pack_cust)._action_confirm()
|
||||
move_stock_pack._action_assign()
|
||||
move_stock_pack.quantity_done = 2.0
|
||||
move_stock_pack._action_done()
|
||||
self.assertEqual(len(move_pack_cust.move_line_ids), 1)
|
||||
|
||||
self.assertAlmostEqual(move_pack_cust.reserved_availability, 1.0)
|
||||
self.assertEqual(move_pack_cust.state, 'partially_available')
|
||||
|
||||
def test_use_unreserved_move_line_1(self):
|
||||
""" Test that validating a stock move linked to an untracked product reserved by another one
|
||||
correctly unreserves the other one.
|
||||
|
||||
@@ -201,39 +201,45 @@ class ProductProduct(models.Model):
|
||||
fifo_automated_values[(row[0], row[1])] = (row[2], row[3], list(row[4]))
|
||||
|
||||
for product in self:
|
||||
if product.cost_method in ['standard', 'average']:
|
||||
qty_available = product.with_context(company_owned=True).qty_available
|
||||
price_used = product.standard_price
|
||||
if to_date:
|
||||
price_used = product.get_history_price(
|
||||
self.env.user.company_id.id,
|
||||
date=to_date,
|
||||
)
|
||||
product.stock_value = price_used * qty_available
|
||||
product.qty_at_date = qty_available
|
||||
elif product.cost_method == 'fifo':
|
||||
if to_date:
|
||||
if product.product_tmpl_id.valuation == 'manual_periodic':
|
||||
domain = [('product_id', '=', product.id), ('date', '<=', to_date)] + StockMove._get_all_base_domain()
|
||||
moves = StockMove.search(domain)
|
||||
if to_date:
|
||||
price_used = product.get_history_price(
|
||||
self.env.user.company_id.id,
|
||||
date=to_date,
|
||||
) if product.cost_method in ['standard', 'average'] else 0.0
|
||||
if product.product_tmpl_id.valuation == 'manual_periodic':
|
||||
domain = [('product_id', '=', product.id), ('date', '<=', to_date)] + StockMove._get_all_base_domain()
|
||||
moves = StockMove.search(domain)
|
||||
product.qty_at_date = product.with_context(company_owned=True, owner_id=False).qty_available
|
||||
if product.cost_method == 'fifo':
|
||||
product.stock_value = sum(moves.mapped('value'))
|
||||
product.qty_at_date = product.with_context(company_owned=True).qty_available
|
||||
product.stock_fifo_manual_move_ids = StockMove.browse(moves.ids)
|
||||
elif product.product_tmpl_id.valuation == 'real_time':
|
||||
valuation_account_id = product.categ_id.property_stock_valuation_account_id.id
|
||||
value, quantity, aml_ids = fifo_automated_values.get((product.id, valuation_account_id)) or (0, 0, [])
|
||||
elif product.cost_method in ['standard', 'average']:
|
||||
product.stock_value = product.qty_at_date * price_used
|
||||
elif product.product_tmpl_id.valuation == 'real_time':
|
||||
valuation_account_id = product.categ_id.property_stock_valuation_account_id.id
|
||||
value, quantity, aml_ids = fifo_automated_values.get((product.id, valuation_account_id)) or (0, 0, [])
|
||||
product.qty_at_date = quantity
|
||||
if product.cost_method == 'fifo':
|
||||
product.stock_value = value
|
||||
product.qty_at_date = quantity
|
||||
product.stock_fifo_real_time_aml_ids = self.env['account.move.line'].browse(aml_ids)
|
||||
else:
|
||||
product.stock_value, moves = product._sum_remaining_values()
|
||||
product.qty_at_date = product.with_context(company_owned=True).qty_available
|
||||
if product.product_tmpl_id.valuation == 'manual_periodic':
|
||||
product.stock_fifo_manual_move_ids = moves
|
||||
elif product.product_tmpl_id.valuation == 'real_time':
|
||||
valuation_account_id = product.categ_id.property_stock_valuation_account_id.id
|
||||
value, quantity, aml_ids = fifo_automated_values.get((product.id, valuation_account_id)) or (0, 0, [])
|
||||
elif product.cost_method in ['standard', 'average']:
|
||||
product.stock_value = quantity * price_used
|
||||
else:
|
||||
if product.product_tmpl_id.valuation == 'manual_periodic':
|
||||
product.qty_at_date = product.with_context(company_owned=True, owner_id=False).qty_available
|
||||
if product.cost_method == 'fifo':
|
||||
product.stock_value, product.stock_fifo_manual_move_ids = product._sum_remaining_values()
|
||||
elif product.cost_method in ['standard', 'average']:
|
||||
product.stock_value = product.qty_at_date * product.standard_price
|
||||
elif product.product_tmpl_id.valuation == 'real_time':
|
||||
valuation_account_id = product.categ_id.property_stock_valuation_account_id.id
|
||||
value, quantity, aml_ids = fifo_automated_values.get((product.id, valuation_account_id)) or (0, 0, [])
|
||||
product.qty_at_date = quantity
|
||||
if product.cost_method == 'fifo':
|
||||
product.stock_value = value
|
||||
product.stock_fifo_real_time_aml_ids = self.env['account.move.line'].browse(aml_ids)
|
||||
elif product.cost_method in ['standard', 'average']:
|
||||
product.stock_value = quantity * product.standard_price
|
||||
|
||||
def action_valuation_at_date_details(self):
|
||||
""" Returns an action with either a list view of all the valued stock moves of `self` if the
|
||||
@@ -404,4 +410,3 @@ class ProductCategory(models.Model):
|
||||
'message': _("Changing your cost method is an important change that will impact your inventory valuation. Are you sure you want to make that change?"),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -596,7 +596,8 @@ class StockMove(models.Model):
|
||||
def _create_account_move_line(self, credit_account_id, debit_account_id, journal_id):
|
||||
self.ensure_one()
|
||||
AccountMove = self.env['account.move']
|
||||
quantity = self.env.context.get('forced_quantity', self.product_qty if self._is_in() else -1 * self.product_qty)
|
||||
quantity = self.env.context.get('forced_quantity', self.product_qty)
|
||||
quantity = quantity if self._is_in() else -1 * quantity
|
||||
|
||||
# Make an informative `ref` on the created account move to differentiate between classic
|
||||
# movements, vacuum and edition of past moves.
|
||||
|
||||
@@ -2079,6 +2079,8 @@ class TestStockValuation(TransactionCase):
|
||||
|
||||
self.assertEqual(len(move1.account_move_ids), 1)
|
||||
|
||||
self.assertAlmostEqual(self.product1.qty_available, 10.0)
|
||||
self.assertAlmostEqual(self.product1.qty_at_date, 10.0)
|
||||
self.assertEqual(self.product1.stock_value, 100)
|
||||
|
||||
# ---------------------------------------------------------------------
|
||||
@@ -2121,6 +2123,8 @@ class TestStockValuation(TransactionCase):
|
||||
|
||||
self.assertEqual(len(move2.account_move_ids), 1)
|
||||
|
||||
self.assertAlmostEqual(self.product1.qty_available, 20.0)
|
||||
self.assertAlmostEqual(self.product1.qty_at_date, 20.0)
|
||||
self.assertEqual(self.product1.stock_value, 220)
|
||||
|
||||
# ---------------------------------------------------------------------
|
||||
@@ -2165,6 +2169,8 @@ class TestStockValuation(TransactionCase):
|
||||
|
||||
self.assertEqual(len(move3.account_move_ids), 1)
|
||||
|
||||
self.assertAlmostEqual(self.product1.qty_available, 12.0)
|
||||
self.assertAlmostEqual(self.product1.qty_at_date, 12.0)
|
||||
self.assertEqual(self.product1.stock_value, 140)
|
||||
|
||||
# ---------------------------------------------------------------------
|
||||
@@ -2198,6 +2204,7 @@ class TestStockValuation(TransactionCase):
|
||||
# Ending
|
||||
# ---------------------------------------------------------------------
|
||||
self.assertEqual(self.product1.qty_available, 6)
|
||||
self.assertAlmostEqual(self.product1.qty_at_date, 6.0)
|
||||
self.assertEqual(self.product1.stock_value, 72)
|
||||
self.assertEqual(sum(self._get_stock_input_move_lines().mapped('debit')), 0)
|
||||
self.assertEqual(sum(self._get_stock_input_move_lines().mapped('credit')), 220)
|
||||
@@ -2553,8 +2560,33 @@ class TestStockValuation(TransactionCase):
|
||||
move2.move_line_ids.qty_done = 1.0
|
||||
move2._action_done()
|
||||
|
||||
self.assertAlmostEqual(self.product1.qty_available, 2.0)
|
||||
self.assertAlmostEqual(self.product1.qty_at_date, 2.0)
|
||||
self.assertAlmostEqual(self.product1.standard_price, 7.5)
|
||||
|
||||
def test_average_perpetual_5(self):
|
||||
''' Set owner on incoming move => no valuation '''
|
||||
self.product1.product_tmpl_id.cost_method = 'average'
|
||||
|
||||
move1 = self.env['stock.move'].create({
|
||||
'name': 'Receive 1 unit at 10',
|
||||
'location_id': self.supplier_location.id,
|
||||
'location_dest_id': self.stock_location.id,
|
||||
'product_id': self.product1.id,
|
||||
'product_uom': self.uom_unit.id,
|
||||
'product_uom_qty': 1.0,
|
||||
'price_unit': 10,
|
||||
})
|
||||
move1._action_confirm()
|
||||
move1._action_assign()
|
||||
move1.move_line_ids.qty_done = 1.0
|
||||
move1.move_line_ids.owner_id = self.owner1.id
|
||||
move1._action_done()
|
||||
|
||||
self.assertAlmostEqual(self.product1.qty_available, 1.0)
|
||||
self.assertAlmostEqual(self.product1.qty_at_date, 0.0)
|
||||
self.assertAlmostEqual(self.product1.stock_value, 0.0)
|
||||
|
||||
def test_average_negative_1(self):
|
||||
""" Test edit in the past. Receive 10, send 20, edit the second move to only send 10.
|
||||
"""
|
||||
@@ -2843,6 +2875,77 @@ class TestStockValuation(TransactionCase):
|
||||
self.assertEqual(move7.value, 100.0)
|
||||
self.assertEqual(self.product1.standard_price, 10)
|
||||
|
||||
def test_average_manual_1(self):
|
||||
''' Set owner on incoming move => no valuation '''
|
||||
self.product1.product_tmpl_id.cost_method = 'average'
|
||||
self.product1.product_tmpl_id.valuation = 'manual_periodic'
|
||||
|
||||
move1 = self.env['stock.move'].create({
|
||||
'name': 'Receive 1 unit at 10',
|
||||
'location_id': self.supplier_location.id,
|
||||
'location_dest_id': self.stock_location.id,
|
||||
'product_id': self.product1.id,
|
||||
'product_uom': self.uom_unit.id,
|
||||
'product_uom_qty': 1.0,
|
||||
'price_unit': 10,
|
||||
})
|
||||
move1._action_confirm()
|
||||
move1._action_assign()
|
||||
move1.move_line_ids.qty_done = 1.0
|
||||
move1.move_line_ids.owner_id = self.owner1.id
|
||||
move1._action_done()
|
||||
|
||||
self.assertAlmostEqual(self.product1.qty_available, 1.0)
|
||||
self.assertAlmostEqual(self.product1.qty_at_date, 0.0)
|
||||
self.assertAlmostEqual(self.product1.stock_value, 0.0)
|
||||
|
||||
def test_standard_perpetual_1(self):
|
||||
''' Set owner on incoming move => no valuation '''
|
||||
self.product1.product_tmpl_id.cost_method = 'standard'
|
||||
|
||||
move1 = self.env['stock.move'].create({
|
||||
'name': 'Receive 1 unit at 10',
|
||||
'location_id': self.supplier_location.id,
|
||||
'location_dest_id': self.stock_location.id,
|
||||
'product_id': self.product1.id,
|
||||
'product_uom': self.uom_unit.id,
|
||||
'product_uom_qty': 1.0,
|
||||
'price_unit': 10,
|
||||
})
|
||||
move1._action_confirm()
|
||||
move1._action_assign()
|
||||
move1.move_line_ids.qty_done = 1.0
|
||||
move1.move_line_ids.owner_id = self.owner1.id
|
||||
move1._action_done()
|
||||
|
||||
self.assertAlmostEqual(self.product1.qty_available, 1.0)
|
||||
self.assertAlmostEqual(self.product1.qty_at_date, 0.0)
|
||||
self.assertAlmostEqual(self.product1.stock_value, 0.0)
|
||||
|
||||
def test_standard_manual_1(self):
|
||||
''' Set owner on incoming move => no valuation '''
|
||||
self.product1.product_tmpl_id.cost_method = 'standard'
|
||||
self.product1.product_tmpl_id.valuation = 'manual_periodic'
|
||||
|
||||
move1 = self.env['stock.move'].create({
|
||||
'name': 'Receive 1 unit at 10',
|
||||
'location_id': self.supplier_location.id,
|
||||
'location_dest_id': self.stock_location.id,
|
||||
'product_id': self.product1.id,
|
||||
'product_uom': self.uom_unit.id,
|
||||
'product_uom_qty': 1.0,
|
||||
'price_unit': 10,
|
||||
})
|
||||
move1._action_confirm()
|
||||
move1._action_assign()
|
||||
move1.move_line_ids.qty_done = 1.0
|
||||
move1.move_line_ids.owner_id = self.owner1.id
|
||||
move1._action_done()
|
||||
|
||||
self.assertAlmostEqual(self.product1.qty_available, 1.0)
|
||||
self.assertAlmostEqual(self.product1.qty_at_date, 0.0)
|
||||
self.assertAlmostEqual(self.product1.stock_value, 0.0)
|
||||
|
||||
def test_change_cost_method_1(self):
|
||||
""" Change the cost method from FIFO to AVCO.
|
||||
"""
|
||||
@@ -2895,6 +2998,8 @@ class TestStockValuation(TransactionCase):
|
||||
move3.move_line_ids.qty_done = 1.0
|
||||
move3._action_done()
|
||||
|
||||
self.assertAlmostEqual(self.product1.qty_available, 19)
|
||||
self.assertAlmostEqual(self.product1.qty_at_date, 19)
|
||||
self.assertEqual(self.product1.stock_value, 240)
|
||||
|
||||
# ---------------------------------------------------------------------
|
||||
@@ -2963,6 +3068,8 @@ class TestStockValuation(TransactionCase):
|
||||
move3.move_line_ids.qty_done = 1.0
|
||||
move3._action_done()
|
||||
|
||||
self.assertAlmostEqual(self.product1.qty_available, 19)
|
||||
self.assertAlmostEqual(self.product1.qty_at_date, 19)
|
||||
self.assertEqual(self.product1.stock_value, 240)
|
||||
|
||||
# ---------------------------------------------------------------------
|
||||
@@ -3036,6 +3143,8 @@ class TestStockValuation(TransactionCase):
|
||||
self.assertEqual(move1.value, 10)
|
||||
self.assertEqual(move1.remaining_value, 10)
|
||||
self.assertEqual(move1.remaining_qty, 1)
|
||||
self.assertAlmostEqual(self.product1.qty_available, 0.0)
|
||||
self.assertAlmostEqual(self.product1.qty_at_date, 2.0)
|
||||
self.assertEqual(self.product1.stock_value, 10)
|
||||
self.assertTrue(len(move1.account_move_ids), 1)
|
||||
|
||||
@@ -3185,8 +3294,10 @@ class TestStockValuation(TransactionCase):
|
||||
move1.move_line_ids.qty_done = 10
|
||||
move1._action_done()
|
||||
move1.date = date2
|
||||
move1.account_move_ids.write({'date': date2})
|
||||
|
||||
self.assertEqual(self.product1.qty_available, 10)
|
||||
self.assertAlmostEqual(self.product1.qty_at_date, 10.0)
|
||||
self.assertEqual(self.product1.stock_value, 100)
|
||||
|
||||
# receive 20
|
||||
@@ -3203,8 +3314,10 @@ class TestStockValuation(TransactionCase):
|
||||
move2.move_line_ids.qty_done = 20
|
||||
move2._action_done()
|
||||
move2.date = date3
|
||||
move2.account_move_ids.write({'date': date3})
|
||||
|
||||
self.assertEqual(self.product1.qty_available, 30)
|
||||
self.assertAlmostEqual(self.product1.qty_at_date, 30.0)
|
||||
self.assertEqual(self.product1.stock_value, 300)
|
||||
|
||||
# send 15
|
||||
@@ -3221,8 +3334,10 @@ class TestStockValuation(TransactionCase):
|
||||
move3.move_line_ids.qty_done = 15
|
||||
move3._action_done()
|
||||
move3.date = date4
|
||||
move3.account_move_ids.write({'date': date4})
|
||||
|
||||
self.assertEqual(self.product1.qty_available, 15)
|
||||
self.assertAlmostEqual(self.product1.qty_at_date, 15.0)
|
||||
self.assertEqual(self.product1.stock_value, 150)
|
||||
|
||||
# set the standard price to 5
|
||||
@@ -3230,6 +3345,7 @@ class TestStockValuation(TransactionCase):
|
||||
self.env['product.price.history'].search([('product_id', '=', self.product1.id)], order='datetime desc, id DESC', limit=1).datetime = date5
|
||||
|
||||
self.assertEqual(self.product1.qty_available, 15)
|
||||
self.assertAlmostEqual(self.product1.qty_at_date, 15.0)
|
||||
self.assertEqual(self.product1.stock_value, 75)
|
||||
|
||||
# send 20
|
||||
@@ -3246,8 +3362,10 @@ class TestStockValuation(TransactionCase):
|
||||
move4.move_line_ids.qty_done = 20
|
||||
move4._action_done()
|
||||
move4.date = date6
|
||||
move4.account_move_ids.write({'date': date6})
|
||||
|
||||
self.assertEqual(self.product1.qty_available, -5)
|
||||
self.assertAlmostEqual(self.product1.qty_at_date, -5.0)
|
||||
self.assertEqual(self.product1.stock_value, -25)
|
||||
|
||||
# set the standard price to 7.5
|
||||
@@ -3268,11 +3386,13 @@ class TestStockValuation(TransactionCase):
|
||||
move5.move_line_ids.qty_done = 100
|
||||
move5._action_done()
|
||||
move5.date = date8
|
||||
move5.account_move_ids.write({'date': date8})
|
||||
|
||||
self.assertEqual(self.product1.qty_available, 95)
|
||||
self.assertAlmostEqual(self.product1.qty_at_date, 95.0)
|
||||
self.assertEqual(self.product1.stock_value, 712.5)
|
||||
|
||||
# Quantity at date
|
||||
# Quantity available at date
|
||||
self.assertEqual(self.product1.with_context(to_date=Date.to_string(date1)).qty_available, 0)
|
||||
self.assertEqual(self.product1.with_context(to_date=Date.to_string(date2)).qty_available, 10)
|
||||
self.assertEqual(self.product1.with_context(to_date=Date.to_string(date3)).qty_available, 30)
|
||||
@@ -3290,12 +3410,25 @@ class TestStockValuation(TransactionCase):
|
||||
self.assertEqual(self.product1.with_context(to_date=Date.to_string(date6)).stock_value, -25)
|
||||
self.assertEqual(self.product1.with_context(to_date=Date.to_string(date8)).stock_value, 712.5)
|
||||
|
||||
# Quantity at date
|
||||
self.assertAlmostEqual(self.product1.with_context(to_date=Date.to_string(date1)).qty_at_date, 0.0)
|
||||
self.assertAlmostEqual(self.product1.with_context(to_date=Date.to_string(date2)).qty_at_date, 10.0)
|
||||
self.assertAlmostEqual(self.product1.with_context(to_date=Date.to_string(date3)).qty_at_date, 30.0)
|
||||
self.assertAlmostEqual(self.product1.with_context(to_date=Date.to_string(date4)).qty_at_date, 15.0)
|
||||
self.assertAlmostEqual(self.product1.with_context(to_date=Date.to_string(date5)).qty_at_date, 15.0)
|
||||
self.assertAlmostEqual(self.product1.with_context(to_date=Date.to_string(date6)).qty_at_date, -5.0)
|
||||
self.assertAlmostEqual(self.product1.with_context(to_date=Date.to_string(date7)).qty_at_date, -5.0)
|
||||
self.assertAlmostEqual(self.product1.with_context(to_date=Date.to_string(date8)).qty_at_date, 95.0)
|
||||
|
||||
# edit the done quantity of move1, decrease it
|
||||
self.assertEqual(self.product1.with_context(to_date=Date.to_string(date2)).qty_available, 10)
|
||||
self.assertAlmostEqual(self.product1.with_context(to_date=Date.to_string(date2)).qty_at_date, 10.0)
|
||||
move1.quantity_done = 5
|
||||
move1.account_move_ids.write({'date': date2})
|
||||
|
||||
# the quantity at date will reflect the change directly
|
||||
self.assertEqual(self.product1.with_context(to_date=Date.to_string(date2)).qty_available, 5)
|
||||
self.assertAlmostEqual(self.product1.with_context(to_date=Date.to_string(date2)).qty_at_date, 5.0)
|
||||
|
||||
# as when we decrease a quantity on a recreipt, we consider it as a out move with the price
|
||||
# of today, the value will be decrease of 100 - (5*7.5)
|
||||
@@ -3307,12 +3440,17 @@ class TestStockValuation(TransactionCase):
|
||||
|
||||
# edit move 4, send 15 instead of 20
|
||||
# we now have +5 + 20 - 15 -20 = -10 * a standard price of 5
|
||||
self.assertAlmostEqual(self.product1.with_context(to_date=Date.to_string(date6)).qty_available, -10.0)
|
||||
self.assertAlmostEqual(self.product1.with_context(to_date=Date.to_string(date6)).qty_at_date, -10.0)
|
||||
self.assertEqual(self.product1.with_context(to_date=Date.to_string(date6)).stock_value, -50)
|
||||
move4.quantity_done = 15
|
||||
move4.account_move_ids.write({'date': date6})
|
||||
|
||||
# -(20*5) + (5*7.5)
|
||||
self.assertEqual(move4.value, -62.5)
|
||||
# we now have +5 + 20 - 15 -15 = -5 * a standard price of 5
|
||||
self.assertAlmostEqual(self.product1.with_context(to_date=Date.to_string(date6)).qty_available, -5.0)
|
||||
self.assertAlmostEqual(self.product1.with_context(to_date=Date.to_string(date6)).qty_at_date, -5.0)
|
||||
self.assertEqual(self.product1.with_context(to_date=Date.to_string(date6)).stock_value, -25)
|
||||
|
||||
def test_at_date_fifo_1(self):
|
||||
@@ -3348,6 +3486,7 @@ class TestStockValuation(TransactionCase):
|
||||
move1.account_move_ids.write({'date': date1})
|
||||
|
||||
self.assertEqual(self.product1.qty_available, 10)
|
||||
self.assertAlmostEqual(self.product1.qty_at_date, 10.0)
|
||||
self.assertEqual(self.product1.stock_value, 100)
|
||||
|
||||
# receive 10@12
|
||||
@@ -3368,6 +3507,7 @@ class TestStockValuation(TransactionCase):
|
||||
move2.account_move_ids.write({'date': date2})
|
||||
|
||||
self.assertEqual(self.product1.qty_available, 20)
|
||||
self.assertAlmostEqual(self.product1.qty_at_date, 20)
|
||||
self.assertEqual(self.product1.stock_value, 220)
|
||||
|
||||
# send 15
|
||||
@@ -3387,6 +3527,7 @@ class TestStockValuation(TransactionCase):
|
||||
move3.account_move_ids.write({'date': date3})
|
||||
|
||||
self.assertEqual(self.product1.qty_available, 5)
|
||||
self.assertAlmostEqual(self.product1.qty_at_date, 5.0)
|
||||
self.assertEqual(self.product1.stock_value, 60)
|
||||
|
||||
# send 20
|
||||
@@ -3406,6 +3547,7 @@ class TestStockValuation(TransactionCase):
|
||||
move4.account_move_ids.write({'date': date4})
|
||||
|
||||
self.assertEqual(self.product1.qty_available, -15)
|
||||
self.assertAlmostEqual(self.product1.qty_at_date, -15.0)
|
||||
self.assertEqual(self.product1.stock_value, -180)
|
||||
|
||||
# receive 100@15
|
||||
@@ -3426,6 +3568,7 @@ class TestStockValuation(TransactionCase):
|
||||
move5.account_move_ids.write({'date': date5})
|
||||
|
||||
self.assertEqual(self.product1.qty_available, 85)
|
||||
self.assertAlmostEqual(self.product1.qty_at_date, 85.0)
|
||||
self.assertEqual(self.product1.stock_value, 1320)
|
||||
|
||||
# run the vacuum to compensate the negative stock move
|
||||
@@ -3433,6 +3576,7 @@ class TestStockValuation(TransactionCase):
|
||||
move4.account_move_ids[0].write({'date': date6})
|
||||
|
||||
self.assertEqual(self.product1.qty_available, 85)
|
||||
self.assertAlmostEqual(self.product1.qty_at_date, 85.0)
|
||||
self.assertEqual(self.product1.stock_value, 1275)
|
||||
|
||||
# Edit the quantity done of move1, increase it.
|
||||
@@ -3505,6 +3649,7 @@ class TestStockValuation(TransactionCase):
|
||||
move1.account_move_ids.write({'date': date1})
|
||||
|
||||
self.assertEqual(self.product1.qty_available, 10)
|
||||
self.assertAlmostEqual(self.product1.qty_at_date, 10.0)
|
||||
self.assertEqual(self.product1.stock_value, 100)
|
||||
|
||||
# receive 10@15
|
||||
@@ -3525,6 +3670,7 @@ class TestStockValuation(TransactionCase):
|
||||
move2.account_move_ids.write({'date': date2})
|
||||
|
||||
self.assertEqual(self.product1.qty_available, 20)
|
||||
self.assertAlmostEqual(self.product1.qty_at_date, 20.0)
|
||||
self.assertEqual(self.product1.stock_value, 250)
|
||||
|
||||
# send 30
|
||||
@@ -3544,6 +3690,7 @@ class TestStockValuation(TransactionCase):
|
||||
move3.account_move_ids.write({'date': date3})
|
||||
|
||||
self.assertEqual(self.product1.qty_available, -10)
|
||||
self.assertAlmostEqual(self.product1.qty_at_date, -10.0)
|
||||
self.assertEqual(self.product1.stock_value, -150)
|
||||
|
||||
# receive 10@20
|
||||
@@ -3564,6 +3711,7 @@ class TestStockValuation(TransactionCase):
|
||||
move4.account_move_ids.write({'date': date4})
|
||||
|
||||
self.assertEqual(self.product1.qty_available, 0)
|
||||
self.assertAlmostEqual(self.product1.qty_at_date, 0.0)
|
||||
self.assertEqual(self.product1.stock_value, 50)
|
||||
|
||||
# receive 10@10
|
||||
@@ -3584,6 +3732,7 @@ class TestStockValuation(TransactionCase):
|
||||
move5.account_move_ids.write({'date': date5})
|
||||
|
||||
self.assertEqual(self.product1.qty_available, 10)
|
||||
self.assertAlmostEqual(self.product1.qty_at_date, 10.0)
|
||||
self.assertEqual(self.product1.stock_value, 150)
|
||||
|
||||
# run the vacuum to compensate the negative stock move
|
||||
@@ -3591,6 +3740,7 @@ class TestStockValuation(TransactionCase):
|
||||
move3.account_move_ids[0].write({'date': date6})
|
||||
|
||||
self.assertEqual(self.product1.qty_available, 10)
|
||||
self.assertAlmostEqual(self.product1.qty_at_date, 10.0)
|
||||
self.assertEqual(self.product1.stock_value, 100)
|
||||
|
||||
# ---------------------------------------------------------------------
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
<templates>
|
||||
<t t-name="date-simple"><t t-esc='value' /></t>
|
||||
<params id="date-simple">{"value": "1988-09-16"}</params>
|
||||
<result id="date-simple">1988-09-16</result>
|
||||
|
||||
<t t-name="datetime-simple"><t t-esc='value' /></t>
|
||||
<params id="datetime-simple">{"value": "1988-09-16 14:00:00"}</params>
|
||||
<result id="datetime-simple">1988-09-16 14:00:00</result>
|
||||
|
||||
<t t-name="datetime-widget-datetime"><t t-esc='value' t-options="{'widget': 'datetime'}" /></t>
|
||||
<params id="datetime-widget-datetime">{"value": "1988-09-16 14:00:00"}</params>
|
||||
<result id="datetime-widget-datetime">09/16/1988 16:00:00</result>
|
||||
|
||||
<t t-name="datetime-widget-date"><t t-esc='value' t-options="{'widget': 'date'}" /></t>
|
||||
<params id="datetime-widget-date">{"value": "1988-09-16 14:00:00"}</params>
|
||||
<result id="datetime-widget-date">09/16/1988</result>
|
||||
|
||||
<t t-name="datetime-widget-date-tz2"><t t-esc='value' t-options="{'widget': 'date'}" /></t>
|
||||
<params id="datetime-widget-date-tz2">{"value": "1988-09-16 01:00:00"}</params>
|
||||
<result id="datetime-widget-date-tz2">09/16/1988</result>
|
||||
|
||||
<t t-name="datetime-widget-date-tz"><t t-esc='value' t-options="{'widget': 'date'}" /></t>
|
||||
<params id="datetime-widget-date-tz">{"value": "1988-09-16 23:00:00"}</params>
|
||||
<result id="datetime-widget-date-tz">09/17/1988</result>
|
||||
|
||||
</templates>
|
||||
@@ -178,7 +178,7 @@ var AbstractWebClient = Widget.extend(ServiceProviderMixin, KeyboardNavigationMi
|
||||
core.bus.on('connection_restored', this, this.on_connection_restored);
|
||||
|
||||
// crash manager integration
|
||||
session.on('error', crash_manager, crash_manager.rpc_error);
|
||||
core.bus.on('rpc_error', crash_manager, crash_manager.rpc_error);
|
||||
window.onerror = function (message, file, line, col, error) {
|
||||
// Scripts injected in DOM (eg: google API's js files) won't return a clean error on window.onerror.
|
||||
// The browser will just give you a 'Script error.' as message and nothing else for security issue.
|
||||
|
||||
@@ -26,9 +26,9 @@ var Loading = Widget.extend({
|
||||
this._super(parent);
|
||||
this.count = 0;
|
||||
this.blocked_ui = false;
|
||||
session.on("request", this, this.request_call);
|
||||
session.on("response", this, this.response_call);
|
||||
session.on("response_failed", this, this.response_call);
|
||||
core.bus.on('rpc_request', this, this.request_call);
|
||||
core.bus.on("rpc_response", this, this.response_call);
|
||||
core.bus.on("rpc_response_failed", this, this.response_call);
|
||||
},
|
||||
destroy: function() {
|
||||
this.on_rpc_event(-this.count);
|
||||
|
||||
@@ -5,7 +5,12 @@ var core = require('web.core');
|
||||
var utils = require('web.utils');
|
||||
var time = require('web.time');
|
||||
|
||||
function genericJsonRpc (fct_name, params, fct) {
|
||||
function genericJsonRpc (fct_name, params, settings, fct) {
|
||||
var shadow = settings.shadow || false;
|
||||
delete settings.shadow;
|
||||
if (! shadow)
|
||||
core.bus.trigger('rpc_request');
|
||||
|
||||
var data = {
|
||||
jsonrpc: "2.0",
|
||||
method: fct_name,
|
||||
@@ -30,11 +35,49 @@ function genericJsonRpc (fct_name, params, fct) {
|
||||
});
|
||||
// FIXME: jsonp?
|
||||
result.abort = function () { if (xhr.abort) xhr.abort(); };
|
||||
return result;
|
||||
|
||||
var p = result.then(function (result) {
|
||||
if (!shadow) {
|
||||
core.bus.trigger('rpc_response');
|
||||
}
|
||||
return result;
|
||||
}, function (type, error, textStatus, errorThrown) {
|
||||
if (type === "server") {
|
||||
if (!shadow) {
|
||||
core.bus.trigger('rpc_response');
|
||||
}
|
||||
if (error.code === 100) {
|
||||
core.bus.trigger('invalidate_session');
|
||||
}
|
||||
return $.Deferred().reject(error, $.Event());
|
||||
} else {
|
||||
if (!shadow) {
|
||||
core.bus.trigger('rpc_response_failed');
|
||||
}
|
||||
var nerror = {
|
||||
code: -32098,
|
||||
message: "XmlHttpRequestError " + errorThrown,
|
||||
data: {
|
||||
type: "xhr"+textStatus,
|
||||
debug: error.responseText,
|
||||
objects: [error, errorThrown]
|
||||
},
|
||||
};
|
||||
return $.Deferred().reject(nerror, $.Event());
|
||||
}
|
||||
});
|
||||
return p.fail(function () { // Allow deferred user to disable rpc_error call in fail
|
||||
p.fail(function (error, event) {
|
||||
if (!event.isDefaultPrevented()) {
|
||||
core.bus.trigger('rpc_error', error, event);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function jsonRpc(url, fct_name, params, settings) {
|
||||
return genericJsonRpc(fct_name, params, function(data) {
|
||||
settings = settings || {};
|
||||
return genericJsonRpc(fct_name, params, settings, function(data) {
|
||||
return $.ajax(url, _.extend({}, settings, {
|
||||
url: url,
|
||||
dataType: 'json',
|
||||
@@ -47,7 +90,7 @@ function jsonRpc(url, fct_name, params, settings) {
|
||||
|
||||
function jsonpRpc(url, fct_name, params, settings) {
|
||||
settings = settings || {};
|
||||
return genericJsonRpc(fct_name, params, function(data) {
|
||||
return genericJsonRpc(fct_name, params, settings, function(data) {
|
||||
var payload_str = JSON.stringify(data, time.date_to_utc);
|
||||
var payload_url = $.param({r:payload_str});
|
||||
var force2step = settings.force2step || false;
|
||||
|
||||
@@ -49,6 +49,7 @@ var Session = core.Class.extend(mixins.EventDispatcherMixin, {
|
||||
this.qweb_mutex = new concurrency.Mutex();
|
||||
this.currencies = {};
|
||||
this._groups_def = {};
|
||||
core.bus.on('invalidate_session', this, this._onInvalidateSession);
|
||||
},
|
||||
setup: function (origin, options) {
|
||||
// must be able to customize server
|
||||
@@ -327,23 +328,17 @@ var Session = core.Class.extend(mixins.EventDispatcherMixin, {
|
||||
rpc: function (url, params, options) {
|
||||
var self = this;
|
||||
options = _.clone(options || {});
|
||||
var shadow = options.shadow || false;
|
||||
options.headers = _.extend({}, options.headers);
|
||||
if (odoo.debug) {
|
||||
options.headers["X-Debug-Mode"] = $.deparam($.param.querystring()).debug;
|
||||
}
|
||||
|
||||
delete options.shadow;
|
||||
|
||||
return self.check_session_id().then(function () {
|
||||
// TODO: remove
|
||||
if (! _.isString(url)) {
|
||||
_.extend(options, url);
|
||||
url = url.url;
|
||||
}
|
||||
// TODO correct handling of timeouts
|
||||
if (! shadow)
|
||||
self.trigger('request');
|
||||
var fct;
|
||||
if (self.origin_server) {
|
||||
fct = ajax.jsonRpc;
|
||||
@@ -362,37 +357,7 @@ var Session = core.Class.extend(mixins.EventDispatcherMixin, {
|
||||
url = self.url(url, null);
|
||||
options.session_id = self.session_id || '';
|
||||
}
|
||||
var p = fct(url, "call", params, options);
|
||||
p = p.then(function (result) {
|
||||
if (! shadow)
|
||||
self.trigger('response');
|
||||
return result;
|
||||
}, function (type, error, textStatus, errorThrown) {
|
||||
if (type === "server") {
|
||||
if (! shadow)
|
||||
self.trigger('response');
|
||||
if (error.code === 100) {
|
||||
self.uid = false;
|
||||
}
|
||||
return $.Deferred().reject(error, $.Event());
|
||||
} else {
|
||||
if (! shadow)
|
||||
self.trigger('response_failed');
|
||||
var nerror = {
|
||||
code: -32098,
|
||||
message: "XmlHttpRequestError " + errorThrown,
|
||||
data: {type: "xhr"+textStatus, debug: error.responseText, objects: [error, errorThrown] }
|
||||
};
|
||||
return $.Deferred().reject(nerror, $.Event());
|
||||
}
|
||||
});
|
||||
return p.fail(function () { // Allow deferred user to disable rpc_error call in fail
|
||||
p.fail(function (error, event) {
|
||||
if (!event.isDefaultPrevented()) {
|
||||
self.trigger('error', error, event);
|
||||
}
|
||||
});
|
||||
});
|
||||
return fct(url, "call", params, options);
|
||||
});
|
||||
},
|
||||
url: function (path, params) {
|
||||
@@ -418,6 +383,17 @@ var Session = core.Class.extend(mixins.EventDispatcherMixin, {
|
||||
getTZOffset: function (date) {
|
||||
return -new Date(date).getTimezoneOffset();
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Handlers
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_onInvalidateSession: function () {
|
||||
this.uid = false;
|
||||
},
|
||||
});
|
||||
|
||||
return Session;
|
||||
|
||||
@@ -491,7 +491,13 @@ var FieldDate = InputField.extend({
|
||||
* @private
|
||||
*/
|
||||
_makeDatePicker: function () {
|
||||
return new datepicker.DateWidget(this, {defaultDate: this.value});
|
||||
return new datepicker.DateWidget(
|
||||
this,
|
||||
_.defaults(
|
||||
this.nodeOptions.datepicker || {},
|
||||
{defaultDate: this.value}
|
||||
)
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
|
||||
@@ -628,9 +628,9 @@ define([
|
||||
}
|
||||
|
||||
var anchors = [];
|
||||
// ODOO: adding this branch to modify existing anchor
|
||||
// ODOO: adding this branch to modify existing anchor if it fully contains the range
|
||||
var ancestor_anchor = dom.ancestor(rng.sc, dom.isAnchor);
|
||||
if(ancestor_anchor) {
|
||||
if(ancestor_anchor && ancestor_anchor === dom.ancestor(rng.ec, dom.isAnchor)) {
|
||||
anchors.push($(ancestor_anchor).html(linkText).get(0));
|
||||
} else if (isTextChanged) {
|
||||
// Create a new link when text changed.
|
||||
|
||||
@@ -474,7 +474,7 @@ function prettify_html(html) {
|
||||
while (i--) space += ' ';
|
||||
return space;
|
||||
},
|
||||
reg = /^<\/?(a|span|font|strong|u|i|strong|b)(\s|>)/i,
|
||||
reg = /^<\/?(a|span|font|u|em|i|strong|b)(\s|>)/i,
|
||||
inline_level = Infinity,
|
||||
tokens = _.compact(_.flatten(_.map(html.split(/</), function (value) {
|
||||
value = value.replace(/\s+/g, ' ').split(/>/);
|
||||
|
||||
@@ -1398,6 +1398,8 @@ var LinkDialog = Dialog.extend({
|
||||
if (dom.ancestor(nodes[i], dom.isImg)) {
|
||||
this.data.images.push(dom.ancestor(nodes[i], dom.isImg));
|
||||
text += '[IMG]';
|
||||
} else if (!is_link && nodes[i].nodeType === 1) {
|
||||
// just use text nodes from listBetween
|
||||
} else if (!is_link && i===0) {
|
||||
text += nodes[i].textContent.slice(so, Infinity);
|
||||
} else if (!is_link && i===nodes.length-1) {
|
||||
|
||||
@@ -6,6 +6,7 @@ import traceback
|
||||
import os
|
||||
import unittest
|
||||
|
||||
import pytz
|
||||
import werkzeug
|
||||
import werkzeug.routing
|
||||
import werkzeug.utils
|
||||
@@ -74,6 +75,10 @@ class Http(models.AbstractModel):
|
||||
context = {}
|
||||
if not request.context.get('tz'):
|
||||
context['tz'] = request.session.get('geoip', {}).get('time_zone')
|
||||
try:
|
||||
pytz.timezone(context['tz'] or '')
|
||||
except pytz.UnknownTimeZoneError:
|
||||
context.pop('tz')
|
||||
|
||||
request.website = request.env['website'].get_current_website() # can use `request.env` since auth methods are called
|
||||
context['website_id'] = request.website.id
|
||||
|
||||
@@ -10,6 +10,6 @@ class IrModel(models.Model):
|
||||
|
||||
@api.multi
|
||||
def unlink(self):
|
||||
self.env.cr.execute(
|
||||
"DELETE FROM ir_model_fields WHERE name='website_id'")
|
||||
self.env.cr.execute("DELETE FROM ir_model_fields WHERE name='website_id'")
|
||||
self.env.cr.execute("DELETE FROM res_config_settings WHERE website_id IS NOT NULL")
|
||||
return super(IrModel, self).unlink()
|
||||
|
||||
@@ -261,6 +261,6 @@ class WebsiteEventController(http.Controller):
|
||||
Attendees._prepare_attendee_values(registration))
|
||||
|
||||
return request.render("website_event.registration_complete", {
|
||||
'attendees': Attendees,
|
||||
'attendees': Attendees.sudo(),
|
||||
'event': event,
|
||||
})
|
||||
|
||||
@@ -59,12 +59,16 @@ var EventRegistrationForm = Widget.extend({
|
||||
$button.attr('disabled', true);
|
||||
return ajax.jsonRpc($form.attr('action'), 'call', post).then(function (modal) {
|
||||
var $modal = $(modal);
|
||||
$modal.modal({backdrop: 'static', keyboard: false});
|
||||
$modal.find('.modal-body > div').removeClass('container'); // retrocompatibility - REMOVE ME in master / saas-19
|
||||
$modal.insertAfter($form).modal();
|
||||
$modal.on('click', '.js_goto_event', function () {
|
||||
$modal.modal('hide');
|
||||
$button.prop('disabled', false);
|
||||
});
|
||||
$modal.on('click', '.close', function () {
|
||||
$button.prop('disabled', false);
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
@@ -50,8 +50,8 @@
|
||||
<input type="text" class="form-control" placeholder="Filter Tracks..." id="event_track_search"/>
|
||||
</div>
|
||||
</section>
|
||||
<t t-set="dayslist" t-value="days.keys()"/>
|
||||
<t t-set="dayslist2" t-value="list(dayslist).sort()"/> <!-- display days in the right order -->
|
||||
<t t-set="dayslist" t-value="list(days.keys())"/>
|
||||
<t t-set="dayslist2" t-value="dayslist.sort()"/> <!-- display days in the right order -->
|
||||
<section class="container" t-foreach="dayslist" t-as="day">
|
||||
<t t-set="locations" t-value="days[day]['locations']"/>
|
||||
<t t-set="dates" t-value="days[day]['dates']"/>
|
||||
|
||||
@@ -422,6 +422,11 @@ class Post(models.Model):
|
||||
raise KarmaError('User karma not sufficient to post an image or link.')
|
||||
return content
|
||||
|
||||
@api.constrains('parent_id')
|
||||
def _check_parent_id(self):
|
||||
if not self._check_recursion():
|
||||
raise ValidationError(_('You cannot create recursive forum posts.'))
|
||||
|
||||
@api.model
|
||||
def create(self, vals):
|
||||
if 'content' in vals and vals.get('forum_id'):
|
||||
|
||||
@@ -20,13 +20,17 @@ class WebsiteLivechat(http.Controller):
|
||||
@http.route('/livechat/channel/<model("im_livechat.channel"):channel>', type='http', auth='public', website=True)
|
||||
def channel_rating(self, channel, **kw):
|
||||
# get the last 100 ratings and the repartition per grade
|
||||
ratings = request.env['rating.rating'].search([('res_model', '=', 'mail.channel'), ('res_id', 'in', channel.sudo().channel_ids.ids)], order='create_date desc', limit=100)
|
||||
repartition = channel.sudo().channel_ids.rating_get_grades()
|
||||
domain = [
|
||||
('res_model', '=', 'mail.channel'), ('res_id', 'in', channel.sudo().channel_ids.ids),
|
||||
('consumed', '=', True), ('rating', '>=', 1),
|
||||
]
|
||||
ratings = request.env['rating.rating'].search(domain, order='create_date desc', limit=100)
|
||||
repartition = channel.sudo().channel_ids.rating_get_grades(domain=domain)
|
||||
|
||||
# compute percentage
|
||||
percentage = dict.fromkeys(['great', 'okay', 'bad'], 0)
|
||||
for grade in repartition:
|
||||
percentage[grade] = repartition[grade] * 100 / sum(repartition.values()) if sum(repartition.values()) else 0
|
||||
percentage[grade] = round(repartition[grade] * 100.0 / sum(repartition.values()), 1) if sum(repartition.values()) else 0
|
||||
|
||||
# the value dict to render the template
|
||||
values = {
|
||||
|
||||
@@ -11,7 +11,7 @@ class MassMailController(MassMailController):
|
||||
def mailing(self, mailing_id, email=None, res_id=None, **post):
|
||||
mailing = request.env['mail.mass_mailing'].sudo().browse(mailing_id)
|
||||
if mailing.exists():
|
||||
if mailing.mailing_model_name == 'mail.mass_mailing.contact':
|
||||
if mailing.mailing_model_real == 'mail.mass_mailing.contact':
|
||||
contacts = request.env['mail.mass_mailing.contact'].sudo().search([('email', '=', email)])
|
||||
return request.render('website_mass_mailing.page_unsubscribe', {
|
||||
'contacts': contacts,
|
||||
|
||||
@@ -11,7 +11,7 @@ class ProductProduct(models.Model):
|
||||
cart_qty = fields.Integer(compute='_compute_cart_qty')
|
||||
|
||||
def _compute_cart_qty(self):
|
||||
website = getattr(request, 'website', None)
|
||||
website = request and getattr(request, 'website', None)
|
||||
if not website:
|
||||
return
|
||||
cart = website.sale_get_order()
|
||||
|
||||
@@ -36,14 +36,15 @@ class ProductWishlist(models.Model):
|
||||
|
||||
@api.model
|
||||
def current(self):
|
||||
"""Get all wishlist items that belong to current user or session."""
|
||||
return self.search([
|
||||
"""Get all wishlist items that belong to current user or session,
|
||||
filter products that are unpublished."""
|
||||
return self.sudo().search([
|
||||
"|",
|
||||
("partner_id", "=", self.env.user.partner_id.id),
|
||||
"&",
|
||||
("partner_id", "=", False),
|
||||
("session", "=", self.env.user.current_session),
|
||||
])
|
||||
]).filtered('product_id.product_tmpl_id.website_published')
|
||||
|
||||
@api.model
|
||||
def _add_to_wishlist(self, pricelist_id, currency_id, website_id, price, product_id, partner_id=False, session=False):
|
||||
|
||||
@@ -13,3 +13,4 @@ List of contributors:
|
||||
Damián Soriano ds@adhoc.com.ar https://github.com/damiansoriano
|
||||
Juan José Scarafía jjs@adhoc.com.ar https://github.com/jjscarafia
|
||||
Nicolás Mac Rouillon nmr@adhoc.com.ar https://github.com/nicomacr
|
||||
Katherine Zaoral kz@adhoc.com.ar https://github.com/zaoral
|
||||
|
||||
@@ -14,7 +14,6 @@ List of contributors:
|
||||
|
||||
Aarón Henríquez ahenriquez@eficent.com https://github.com/aheficent
|
||||
Lois Rilo lois.rilo@eficent.com https://github.com/lreficent
|
||||
Roser Garcia roser.garcia@eficent.com https://github.com/rgarnau
|
||||
Miquel Raich miquel.raich@eficent.com https://github.com/mreficent
|
||||
Jordi Ballester jordi.ballester@eficent.com https://github.com/jbeficent
|
||||
Hector Villarreal hector.villarreal@eficent.com https://github.com/hveficent
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
Serbia, 2018-06-11
|
||||
|
||||
Modoolar agrees to the terms of the Odoo Corporate Contributor License
|
||||
Agreement v1.0.
|
||||
|
||||
I declare that I am authorized and able to make this agreement and sign this
|
||||
declaration.
|
||||
|
||||
Signed,
|
||||
|
||||
Petar Najman petar.najman@modoolar.com https://github.com/pnajman-modoolar
|
||||
|
||||
List of contributors:
|
||||
|
||||
- Aleksandar Gajić aleksandar.gajic@modoolar.com https://github.com/agajic-modoolar
|
||||
- Igor Jovanović igor.jovanovic@modoolar.com https://github.com/ijovanovic-modoolar
|
||||
- Mladen Meseldžija mladen.meseldzija@modoolar.com https://github.com/mmeseldzija-modoolar
|
||||
- Petar Najman petar.najman@modoolar.com https://github.com/pnajman-modoolar
|
||||
- Slađan Kantar sladjan.kantar@modoolar.com https://github.com/skantar
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user