Browse Source

[12.0][MIG] date_range

pull/25/head
Andrea 6 years ago
parent
commit
a40a8ddfa5
  1. 10
      date_range/README.rst
  2. 1
      date_range/__init__.py
  3. 9
      date_range/__manifest__.py
  4. 9
      date_range/migrations/11.0.2.0.0/noupdate_changes.xml
  5. 3
      date_range/migrations/11.0.2.0.0/openupgrade_analysis.txt
  6. 4
      date_range/migrations/11.0.2.0.0/openupgrade_analysis_work.txt
  7. 11
      date_range/migrations/11.0.2.0.0/post-migration.py
  8. 1
      date_range/models/__init__.py
  9. 14
      date_range/models/date_range.py
  10. 14
      date_range/models/date_range_type.py
  11. 1
      date_range/readme/CONTRIBUTORS.rst
  12. 2
      date_range/readme/INSTALL.rst
  13. 210
      date_range/static/src/js/date_range.js
  14. 1
      date_range/tests/__init__.py
  15. 10
      date_range/tests/test_date_range.py
  16. 13
      date_range/tests/test_date_range_generator.py
  17. 7
      date_range/tests/test_date_range_type.py
  18. 1
      date_range/wizard/__init__.py
  19. 22
      date_range/wizard/date_range_generator.py

10
date_range/README.rst

@ -14,13 +14,13 @@ Date Range
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fserver--ux-lightgray.png?logo=github
:target: https://github.com/OCA/server-ux/tree/11.0/date_range
:target: https://github.com/OCA/server-ux/tree/12.0/date_range
:alt: OCA/server-ux
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/server-ux-11-0/server-ux-11-0-date_range
:alt: Translate me on Weblate
.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png
:target: https://runbot.odoo-community.org/runbot/250/11.0
:target: https://runbot.odoo-community.org/runbot/250/12.0
:alt: Try me on Runbot
|badge1| |badge2| |badge3| |badge4| |badge5|
@ -97,7 +97,7 @@ Bug Tracker
Bugs are tracked on `GitHub Issues <https://github.com/OCA/server-ux/issues>`_.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us smashing it by providing a detailed and welcomed
`feedback <https://github.com/OCA/server-ux/issues/new?body=module:%20date_range%0Aversion:%2011.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
`feedback <https://github.com/OCA/server-ux/issues/new?body=module:%20date_range%0Aversion:%2012.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
Do not contact contributors directly about support or help with technical issues.
@ -133,10 +133,10 @@ promote its widespread use.
:target: https://github.com/lmignon
:alt: lmignon
Current `maintainer <https://odoo-community.org/page/maintainer-role>`__:
Current `maintainer <https://odoo-community.org/page/maintainer-role>`_:
|maintainer-lmignon|
This module is part of the `OCA/server-ux <https://github.com/OCA/server-ux/tree/11.0/date_range>`_ project on GitHub.
This module is part of the `OCA/server-ux <https://github.com/OCA/server-ux/tree/12.0/date_range>`_ project on GitHub.
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

1
date_range/__init__.py

@ -1,4 +1,3 @@
# © 2016 ACSONE SA/NV (<http://acsone.eu>)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from . import models

9
date_range/__manifest__.py

@ -1,14 +1,13 @@
# © 2016 ACSONE SA/NV (<http://acsone.eu>)
# Copyright 2016 ACSONE SA/NV (<http://acsone.eu>)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
{
"name": "Date Range",
"summary": "Manage all kind of date range",
"version": "11.0.2.0.0",
"version": "12.0.1.0.0",
"category": "Uncategorized",
"website": "https://github.com/oca/server-ux",
"author": "ACSONE SA/NV, Odoo Community Association (OCA)",
"license": "AGPL-3",
"application": False,
"installable": True,
"depends": [
"web",
@ -23,6 +22,6 @@
"qweb": [
"static/src/xml/date_range.xml",
],
'development_status': 'Mature',
'maintainers': ['lmignon'],
"development_status": "Mature",
"maintainers": ["lmignon"],
}

9
date_range/migrations/11.0.2.0.0/noupdate_changes.xml

@ -1,9 +0,0 @@
<?xml version='1.0' encoding='utf-8'?>
<odoo>
<record id="date_range_type_comp_rule" model="ir.rule">
<field name="domain_force"> ['|',('company_id','child_of',[user.company_id.id]),('company_id','=',False)]</field>
</record>
<record id="date_range_comp_rule" model="ir.rule">
<field name="domain_force"> ['|',('company_id','child_of',[user.company_id.id]),('company_id','=',False)]</field>
</record>
</odoo>

3
date_range/migrations/11.0.2.0.0/openupgrade_analysis.txt

@ -1,3 +0,0 @@
---Fields in module 'date_range'---
---XML records in module 'date_range'---
NEW ir.rule: date_range.date_range_generator_comp_rule (noupdate)

4
date_range/migrations/11.0.2.0.0/openupgrade_analysis_work.txt

@ -1,4 +0,0 @@
---Fields in module 'date_range'---
---XML records in module 'date_range'---
NEW ir.rule: date_range.date_range_generator_comp_rule (noupdate)
# NOTHING TO DO

11
date_range/migrations/11.0.2.0.0/post-migration.py

@ -1,11 +0,0 @@
# Copyright 2017 Eficent <http://www.eficent.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from openupgradelib import openupgrade
@openupgrade.migrate()
def migrate(env, version):
openupgrade.load_data(
env.cr, 'date_range', 'migrations/11.0.2.0.0/noupdate_changes.xml',
)

1
date_range/models/__init__.py

@ -1,4 +1,3 @@
# © 2016 ACSONE SA/NV (<http://acsone.eu>)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from . import date_range_type

14
date_range/models/date_range.py

@ -1,13 +1,13 @@
# © 2016 ACSONE SA/NV (<http://acsone.eu>)
# Copyright 2016 ACSONE SA/NV (<http://acsone.eu>)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import api, fields, models
from odoo.tools.translate import _
from odoo import _, api, fields, models
from odoo.exceptions import ValidationError
class DateRange(models.Model):
_name = "date.range"
_description = "Date Range"
_order = "type_name,date_start"
@api.model
@ -22,7 +22,7 @@ class DateRange(models.Model):
ondelete='restrict', domain="['|', ('company_id', '=', company_id), "
"('company_id', '=', False)]")
type_name = fields.Char(
string='Type', related='type_id.name', readonly=True, store=True)
related='type_id.name', readonly=True, store=True, string="Type Name")
company_id = fields.Many2one(
comodel_name='res.company', string='Company', index=1,
default=_default_company)
@ -34,7 +34,7 @@ class DateRange(models.Model):
('date_range_uniq', 'unique (name,type_id, company_id)',
'A date range must be unique per company !')]
@api.onchange('company_id')
@api.onchange('company_id', 'type_id')
def _onchange_company_id(self):
if self.company_id and self.type_id.company_id and \
self.type_id.company_id != self.company_id:
@ -54,9 +54,7 @@ class DateRange(models.Model):
@api.constrains('type_id', 'date_start', 'date_end', 'company_id')
def _validate_range(self):
for this in self:
start = fields.Date.from_string(this.date_start)
end = fields.Date.from_string(this.date_end)
if start > end:
if this.date_start > this.date_end:
raise ValidationError(
_("%s is not a valid range (%s > %s)") % (
this.name, this.date_start, this.date_end))

14
date_range/models/date_range_type.py

@ -1,13 +1,13 @@
# © 2016 ACSONE SA/NV (<http://acsone.eu>)
# Copyright 2016 ACSONE SA/NV (<http://acsone.eu>)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import api, fields, models
from odoo.tools.translate import _
from odoo import _, api, fields, models
from odoo.exceptions import ValidationError
class DateRangeType(models.Model):
_name = "date.range.type"
_description = "Date Range Type"
@api.model
def _default_company(self):
@ -18,8 +18,8 @@ class DateRangeType(models.Model):
help="If sets date range of same type must not overlap.",
default=False)
active = fields.Boolean(
help="The active field allows you to hide the date range without "
"removing it.", default=True)
help="The active field allows you to hide the date range type "
"without removing it.", default=True)
company_id = fields.Many2one(
comodel_name='res.company', string='Company', index=1,
default=_default_company)
@ -35,10 +35,10 @@ class DateRangeType(models.Model):
for rec in self.sudo():
if not rec.company_id:
continue
if bool(self.date_range_ids.filtered(
if bool(rec.date_range_ids.filtered(
lambda r: r.company_id and
r.company_id != rec.company_id)):
raise ValidationError(
_('You cannot change the company, as this '
'Date Range Type is assigned to Date Range '
'(%s).') % (self.date_range_ids.name_get()[0][1]))
'(%s).') % (rec.date_range_ids.name_get()[0][1]))

1
date_range/readme/CONTRIBUTORS.rst

@ -1,3 +1,4 @@
* Laurent Mignon <laurent.mignon@acsone.eu>
* Alexis de Lattre <alexis.delattre@akretion.com>
* Miquel Raïch <miquel.raich@eficent.com>
* Andrea Stirpe <a.stirpe@onestein.nl>

2
date_range/readme/INSTALL.rst

@ -1,3 +1 @@
The addon use the daterange method from postgres. This method is supported as of postgresql 9.2

210
date_range/static/src/js/date_range.js

@ -1,125 +1,125 @@
/* © 2016 ACSONE SA/NV (<http://acsone.eu>)
/* Copyright 2016 ACSONE SA/NV (<http://acsone.eu>)
* License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). */
odoo.define('date_range.search_filters', function (require) {
"use strict";
var core = require('web.core');
var data = require('web.data');
var filters = require('web.search_filters');
var rpc = require('web.rpc');
var framework = require('web.framework');
var _t = core._t;
filters.ExtendedSearchProposition.include({
select_field: function(field) {
this._super.apply(this, arguments);
this.is_date_range_selected = false;
this.is_date = field.type === 'date' || field.type === 'datetime';
this.$value = this.$el.find('.searchview_extended_prop_value, .o_searchview_extended_prop_value');
if (this.is_date){
var ds = new data.DataSetSearch(this, 'date.range.type', this.context, [[1, '=', 1]]);
ds.read_slice(['name'], {}).done(this.proxy('add_date_range_types_operator'));
}
},
add_date_range_types_operator: function(date_range_types){
var self = this;
_.each(date_range_types, function(drt) {
$('<option>', {value: 'drt_' + drt.id})
.text(_('in ') + drt.name)
.appendTo(self.$el.find('.searchview_extended_prop_op, .o_searchview_extended_prop_op'));
});
},
operator_changed: function (e) {
var val = $(e.target).val();
this.is_date_range_selected = val.startsWith('drt_');
if (this.is_date_range_selected){
var type_id = val.replace('drt_', '');
this.date_range_type_operator_selected(type_id);
return;
}
this._super.apply(this, arguments);
},
date_range_type_operator_selected: function(type_id){
this.$value.empty().show();
var ds = new data.DataSetSearch(this, 'date.range', this.context, [['type_id', '=', parseInt(type_id)]]);
ds.read_slice(['name','date_start', 'date_end'], {}).done(this.proxy('on_range_type_selected'));
},
on_range_type_selected: function(date_range_values){
this.value = new filters.ExtendedSearchProposition.DateRange(this, this.value.field, date_range_values);
this.value.appendTo(this.$value);
if (!this.$el.hasClass('o_filter_condition')){
this.$value.find('.date-range-select').addClass('form-control');
}
this.value.on_range_selected();
},
get_filter: function () {
var res = this._super.apply(this, arguments);
if (this.is_date_range_selected){
// in case of date.range, the domain is provided by the server and we don't
// want to put nest the returned value into an array.
res.attrs.domain = this.value.domain;
"use strict";
var core = require('web.core');
var data = require('web.data');
var filters = require('web.search_filters');
var rpc = require('web.rpc');
var framework = require('web.framework');
var _t = core._t;
filters.ExtendedSearchProposition.include({
select_field: function (field) {
this._super.apply(this, arguments);
this.is_date_range_selected = false;
this.is_date = field.type === 'date' || field.type === 'datetime';
this.$value = this.$el.find('.searchview_extended_prop_value, .o_searchview_extended_prop_value');
if (this.is_date) {
var ds = new data.DataSetSearch(this, 'date.range.type', this.context, [[1, '=', 1]]);
ds.read_slice(['name'], {}).done(this.proxy('add_date_range_types_operator'));
}
},
add_date_range_types_operator: function (date_range_types) {
var self = this;
_.each(date_range_types, function (drt) {
$('<option>', {value: 'drt_' + drt.id})
.text(_('in ') + drt.name)
.appendTo(self.$el.find('.searchview_extended_prop_op, .o_searchview_extended_prop_op'));
});
},
operator_changed: function (e) {
var val = $(e.target).val();
this.is_date_range_selected = val.startsWith('drt_');
if (this.is_date_range_selected) {
var type_id = val.replace('drt_', '');
this.date_range_type_operator_selected(type_id);
return;
}
this._super.apply(this, arguments);
},
date_range_type_operator_selected: function (type_id) {
this.$value.empty().show();
var ds = new data.DataSetSearch(this, 'date.range', this.context, [['type_id', '=', parseInt(type_id, 10)]]);
ds.read_slice(['name','date_start', 'date_end'], {}).done(this.proxy('on_range_type_selected'));
},
on_range_type_selected: function (date_range_values) {
this.value = new filters.ExtendedSearchProposition.DateRange(this, this.value.field, date_range_values);
this.value.appendTo(this.$value);
if (!this.$el.hasClass('o_filter_condition')) {
this.$value.find('.date-range-select').addClass('form-control');
}
this.value.on_range_selected();
},
get_filter: function () {
var res = this._super.apply(this, arguments);
if (this.is_date_range_selected) {
// In case of date.range, the domain is provided by the server and we don't
// Want to put nest the returned value into an array.
res.attrs.domain = this.value.domain;
}
return res;
}
return res;
},
});
});
/**
/**
Since Odoo 11, The Field class used as base class for all specialized filter
widgets is no more exposed by 'web.search_filters'. To create our own class we
extend the more simple class available into the search_filters_registry as base
class
*/
filters.ExtendedSearchProposition.DateRange = core.search_filters_registry.get('id').extend({
template: 'SearchView.extended_search.dateRange.selection',
events: {
'change': 'on_range_selected',
},
init: function (parent, field, date_range_values) {
this._super(parent, field);
this.date_range_values = date_range_values;
},
toString: function () {
var select = this.$el[0];
var option = select.options[select.selectedIndex];
return option.label || option.text;
},
get_value: function() {
return parseInt(this.$el.val());
},
on_range_selected: function(e){
var self = this;
self.domain = '';
framework.blockUI();
return rpc.query({
filters.ExtendedSearchProposition.DateRange = core.search_filters_registry.get('id').extend({
template: 'SearchView.extended_search.dateRange.selection',
events: {
'change': 'on_range_selected'
},
init: function (parent, field, date_range_values) {
this._super(parent, field);
this.date_range_values = date_range_values;
},
toString: function () {
var select = this.$el[0];
var option = select.options[select.selectedIndex];
return option.label || option.text;
},
get_value: function () {
return parseInt(this.$el.val(), 10);
},
on_range_selected: function (e) {
var self = this;
self.domain = '';
framework.blockUI();
return rpc.query({
args: [this.get_value()],
kwargs: {
field_name: this.field.name
},
model: 'date.range',
method: 'get_domain',
method: 'get_domain'
})
.then(function (domain) {
framework.unblockUI();
self.domain = domain;
});
},
get_domain: function (field, operator) {
return this.domain;
},
.then(function (domain) {
framework.unblockUI();
self.domain = domain;
});
},
get_domain: function (field, operator) {
return this.domain;
}
});
});
});

1
date_range/tests/__init__.py

@ -1,4 +1,3 @@
# © 2016 ACSONE SA/NV (<http://acsone.eu>)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from . import test_date_range_type

10
date_range/tests/test_date_range.py

@ -1,8 +1,10 @@
# © 2016 ACSONE SA/NV (<http://acsone.eu>)
# Copyright 2016 ACSONE SA/NV (<http://acsone.eu>)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
from odoo.tests.common import TransactionCase
import datetime
from odoo.exceptions import ValidationError
from odoo.tests.common import TransactionCase
class DateRangeTest(TransactionCase):
@ -105,8 +107,8 @@ class DateRangeTest(TransactionCase):
# By default the domain include limits
self.assertEqual(
domain,
[('my_field', '>=', '2015-01-01'),
('my_field', '<=', '2015-12-31')])
[('my_field', '>=', datetime.date(2015, 1, 1)),
('my_field', '<=', datetime.date(2015, 12, 31))])
def test_date_range_multicompany_1(self):
dr = self.date_range.new({

13
date_range/tests/test_date_range_generator.py

@ -1,10 +1,13 @@
# © 2016 ACSONE SA/NV (<http://acsone.eu>)
# Copyright 2016 ACSONE SA/NV (<http://acsone.eu>)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)nses/agpl).
from odoo.tests.common import TransactionCase
from odoo.exceptions import ValidationError
import datetime
from dateutil.rrule import MONTHLY
from odoo.exceptions import ValidationError
from odoo.tests.common import TransactionCase
class DateRangeGeneratorTest(TransactionCase):
post_install = True
@ -43,8 +46,8 @@ class DateRangeGeneratorTest(TransactionCase):
[('type_id', '=', self.type.id)])
self.assertEqual(len(ranges), 4)
range4 = ranges[3]
self.assertEqual(range4.date_start, '1943-10-01')
self.assertEqual(range4.date_end, '1943-12-31')
self.assertEqual(range4.date_start, datetime.date(1943, 10, 1))
self.assertEqual(range4.date_end, datetime.date(1943, 12, 31))
self.assertEqual(range4.type_id, self.type)
def test_generator_multicompany_1(self):

7
date_range/tests/test_date_range_type.py

@ -1,10 +1,11 @@
# © 2016 ACSONE SA/NV (<http://acsone.eu>)
# Copyright 2016 ACSONE SA/NV (<http://acsone.eu>)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
from odoo.tests.common import TransactionCase
from odoo.tools import mute_logger
from psycopg2 import IntegrityError
from odoo.exceptions import ValidationError
from odoo.tests.common import TransactionCase
from odoo.tools import mute_logger
class DateRangeTypeTest(TransactionCase):

1
date_range/wizard/__init__.py

@ -1,4 +1,3 @@
# © 2016 ACSONE SA/NV (<http://acsone.eu>)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from . import date_range_generator

22
date_range/wizard/date_range_generator.py

@ -1,19 +1,16 @@
# © 2016 ACSONE SA/NV (<http://acsone.eu>)
# Copyright 2016 ACSONE SA/NV (<http://acsone.eu>)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import api, fields, models
from odoo.tools.translate import _
from odoo.exceptions import ValidationError
from dateutil.rrule import (rrule,
YEARLY,
MONTHLY,
WEEKLY,
DAILY)
from dateutil.relativedelta import relativedelta
from dateutil.rrule import DAILY, MONTHLY, WEEKLY, YEARLY, rrule
from odoo import _, api, fields, models
from odoo.exceptions import ValidationError
class DateRangeGenerator(models.TransientModel):
_name = 'date.range.generator'
_description = 'Date Range Generator'
@api.model
def _default_company(self):
@ -41,17 +38,16 @@ class DateRangeGenerator(models.TransientModel):
def _compute_date_ranges(self):
self.ensure_one()
vals = rrule(freq=self.unit_of_time, interval=self.duration_count,
dtstart=fields.Date.from_string(self.date_start),
dtstart=self.date_start,
count=self.count+1)
vals = list(vals)
date_ranges = []
count_digits = len(str(self.count))
for idx, dt_start in enumerate(vals[:-1]):
date_start = fields.Date.to_string(dt_start.date())
date_start = dt_start.date()
# always remove 1 day for the date_end since range limits are
# inclusive
dt_end = vals[idx+1].date() - relativedelta(days=1)
date_end = fields.Date.to_string(dt_end)
date_end = vals[idx+1].date() - relativedelta(days=1)
date_ranges.append({
'name': '%s%0*d' % (
self.name_prefix, count_digits, idx + 1),

Loading…
Cancel
Save