mirror of
https://gitlab.com/flectra-hq/flectra.git
synced 2026-10-25 07:27:39 -05:00
[PATCH] Upstream patch - 17112021
This commit is contained in:
1 parent
74d2cc8fbe
commit
c4ef5487e1
22 files changed
+176
-41
No files matched your search
@@ -300,7 +300,7 @@ class HolidaysAllocation(models.Model):
|
||||
def _compute_can_approve(self):
|
||||
for allocation in self:
|
||||
try:
|
||||
if allocation.state == 'confirm' and allocation.validation_type == 'both':
|
||||
if allocation.state == 'confirm' and allocation.holiday_status_id.allocation_type == "fixed_allocation" and allocation.validation_type == 'both':
|
||||
allocation._check_approval_update('validate1')
|
||||
else:
|
||||
allocation._check_approval_update('validate')
|
||||
|
||||
@@ -24,6 +24,7 @@ Allows to contact directly the employee in case of unjustified absence.
|
||||
'views/hr_employee_views.xml',
|
||||
'data/sms_data.xml',
|
||||
'data/mail_data.xml',
|
||||
'data/ir_cron.xml',
|
||||
],
|
||||
'demo': [],
|
||||
'installable': True,
|
||||
|
||||
@@ -9,6 +9,7 @@ import io
|
||||
from flectra import api, fields, models, _
|
||||
from flectra.exceptions import UserError
|
||||
from flectra.tools import float_is_zero, pycompat
|
||||
from flectra.tools.misc import get_lang
|
||||
|
||||
|
||||
class AccountFrFec(models.TransientModel):
|
||||
@@ -304,7 +305,7 @@ class AccountFrFec(models.TransientModel):
|
||||
sql_query = '''
|
||||
SELECT
|
||||
replace(replace(aj.code, '|', '/'), '\t', '') AS JournalCode,
|
||||
replace(replace(aj.name, '|', '/'), '\t', '') AS JournalLib,
|
||||
replace(replace(COALESCE(aj__name.value, aj.name), '|', '/'), '\t', '') AS JournalLib,
|
||||
replace(replace(am.name, '|', '/'), '\t', '') AS EcritureNum,
|
||||
TO_CHAR(am.date, 'YYYYMMDD') AS EcritureDate,
|
||||
aa.code AS CompteNum,
|
||||
@@ -346,6 +347,11 @@ class AccountFrFec(models.TransientModel):
|
||||
LEFT JOIN account_move am ON am.id=aml.move_id
|
||||
LEFT JOIN res_partner rp ON rp.id=aml.partner_id
|
||||
JOIN account_journal aj ON aj.id = am.journal_id
|
||||
LEFT JOIN ir_translation aj__name ON aj__name.res_id = aj.id
|
||||
AND aj__name.type = 'model'
|
||||
AND aj__name.name = 'account.journal,name'
|
||||
AND aj__name.lang = %s
|
||||
AND aj__name.value != ''
|
||||
JOIN account_account aa ON aa.id = aml.account_id
|
||||
LEFT JOIN account_account_type aat ON aa.user_type_id = aat.id
|
||||
LEFT JOIN res_currency rc ON rc.id = aml.currency_id
|
||||
@@ -369,8 +375,9 @@ class AccountFrFec(models.TransientModel):
|
||||
am.name,
|
||||
aml.id
|
||||
'''
|
||||
lang = self.env.user.lang or get_lang(self.env).code
|
||||
self._cr.execute(
|
||||
sql_query, (self.date_from, self.date_to, company.id))
|
||||
sql_query, (lang, self.date_from, self.date_to, company.id))
|
||||
|
||||
for row in self._cr.fetchall():
|
||||
rows_to_write.append(list(row))
|
||||
|
||||
@@ -22,6 +22,7 @@ flectra.define('mass_mailing.mass_mailing_tour', function (require) {
|
||||
edition: 'community',
|
||||
}, {
|
||||
trigger: '.o-kanban-button-new',
|
||||
extra_trigger: '.oe_kanban_mass_mailing',
|
||||
content: _t("Start by creating your first <b>Mailing</b>."),
|
||||
position: 'bottom',
|
||||
}, {
|
||||
|
||||
@@ -11,14 +11,14 @@ class Attendee(models.Model):
|
||||
_name = 'calendar.attendee'
|
||||
_inherit = 'calendar.attendee'
|
||||
|
||||
def _send_mail_to_attendees(self, mail_template, force_send=False, ignore_recurrence=False):
|
||||
def _send_mail_to_attendees(self, template_xmlid, force_send=False, ignore_recurrence=False):
|
||||
""" Override the super method
|
||||
If not synced with Microsoft Outlook, let Flectra in charge of sending emails
|
||||
Otherwise, Microsoft Outlook will send them
|
||||
"""
|
||||
with microsoft_calendar_token(self.env.user.sudo()) as token:
|
||||
if not token:
|
||||
super()._send_mail_to_attendees(mail_template, force_send, ignore_recurrence)
|
||||
super()._send_mail_to_attendees(template_xmlid, force_send, ignore_recurrence)
|
||||
|
||||
def do_tentative(self):
|
||||
# Synchronize event after state change
|
||||
|
||||
@@ -214,7 +214,9 @@ class MicrosoftSync(models.AbstractModel):
|
||||
normal_events += [recurrent_event.flectra_id(self.env)]
|
||||
else:
|
||||
value = self.env['calendar.event']._microsoft_to_flectra_values(recurrent_event, (), default_values)
|
||||
self.env['calendar.event'].browse(recurrent_event.flectra_id(self.env)).with_context(no_mail_to_attendees=True, mail_create_nolog=True).write(dict(value, need_sync_m=False))
|
||||
event = self.env['calendar.event'].browse(recurrent_event.flectra_id(self.env)).exists()
|
||||
if event:
|
||||
event.with_context(no_mail_to_attendees=True, mail_create_nolog=True).write(dict(value, need_sync_m=False))
|
||||
if value.get('start') and value.get('stop'):
|
||||
values[(self.id, value.get('start'), value.get('stop'))] = dict(value, need_sync_m=False)
|
||||
|
||||
|
||||
@@ -7,11 +7,13 @@ import pytz
|
||||
from datetime import datetime, date
|
||||
from flectra.tests.common import TransactionCase
|
||||
from dateutil.relativedelta import relativedelta
|
||||
from freezegun import freeze_time
|
||||
|
||||
|
||||
class TestSyncMicrosoft2Flectra(TransactionCase):
|
||||
|
||||
@property
|
||||
@freeze_time('2020-06-03')
|
||||
def now(self):
|
||||
return pytz.utc.localize(datetime.now()).isoformat()
|
||||
|
||||
@@ -29,10 +31,12 @@ class TestSyncMicrosoft2Flectra(TransactionCase):
|
||||
|
||||
self.datetime_future = pytz.utc.localize(datetime.now() + relativedelta(days=1)).isoformat()
|
||||
|
||||
@freeze_time('2020-06-03')
|
||||
def sync(self, events):
|
||||
|
||||
self.env['calendar.event']._sync_microsoft2flectra(events)
|
||||
|
||||
@freeze_time('2020-06-03')
|
||||
def test_new_microsoft_recurrence(self):
|
||||
|
||||
recurrence = self.env['calendar.recurrence'].search([('microsoft_id', '=', self.recurrence_id)])
|
||||
@@ -49,6 +53,7 @@ class TestSyncMicrosoft2Flectra(TransactionCase):
|
||||
self.assertEqual(events[2].start, datetime(2020, 5, 5, 14, 30))
|
||||
self.assertEqual(events[2].stop, datetime(2020, 5, 5, 16, 00))
|
||||
|
||||
@freeze_time('2020-06-03')
|
||||
def test_microsoft_recurrence_delete_one_event(self):
|
||||
values = [
|
||||
{'@odata.type': '#microsoft.graph.event', '@odata.etag': 'W/"ZS2uEVAVyU6BMZ3m6cHmtgAADIaZKQ=="', 'createdDateTime': '2020-05-06T07:03:49.1444085Z', 'lastModifiedDateTime': self.datetime_future, 'changeKey': 'ZS2uEVAVyU6BMZ3m6cHmtgAADIaZKQ==', 'categories': [], 'originalStartTimeZone': 'Romance Standard Time', 'originalEndTimeZone': 'Romance Standard Time', 'iCalUId': '040000008200E00074C5B7101A82E00800000000874F057E7423D601000000000000000010000000C6918C4B44D2D84586351FEC8B1B7F8C', 'reminderMinutesBeforeStart': 15, 'isReminderOn': True, 'hasAttachments': False, 'subject': 'My recurrent event', 'bodyPreview': '', 'importance': 'normal', 'sensitivity': 'normal', 'isAllDay': False, 'isCancelled': False, 'isOrganizer': True, 'IsRoomRequested': False, 'AutoRoomBookingStatus': 'None', 'responseRequested': True, 'seriesMasterId': None, 'showAs': 'busy', 'type': 'seriesMaster', 'webLink': 'https://outlook.live.com/owa/?itemid=AQ8PojGtrADQATM3ZmYAZS0yY2MAMC00MDg1LTAwAi0wMAoARgAAA0By7X03vaNKv1GnWYTbFYAHAGUtrhFQFclOgTGd5unB5rYAAAIBDQAAAGUtrhFQFclOgTGd5unB5rYAAAALLLTEAAAA&exvsurl=1&path=/calendar/item', 'onlineMeetingUrl': None, 'isOnlineMeeting': False, 'onlineMeetingProvider': 'unknown', 'AllowNewTimeProposals': True, 'IsDraft': False, 'id': 'AQ8PojGtrADQATM3ZmYAZS0yY2MAMC00MDg1LTAwAi0wMAoARgAAA0By7X03vaNKv1GnWYTbFYAHAGUtrhFQFclOgTGd5unB5rYAAAIBDQAAAGUtrhFQFclOgTGd5unB5rYAAAALLLTEAAAA', 'responseStatus': {'response': 'organizer', 'time': '0001-01-01T00:00:00Z'}, 'body': {'contentType': 'html', 'content': ''}, 'start': {'dateTime': '2020-05-03T14:30:00.0000000', 'timeZone': 'UTC'}, 'end': {'dateTime': '2020-05-03T16:00:00.0000000', 'timeZone': 'UTC'}, 'location': {'displayName': '', 'locationType': 'default', 'uniqueIdType': 'unknown', 'address': {}, 'coordinates': {}}, 'locations': [], 'recurrence': {'pattern': {'type': 'daily', 'interval': 1, 'month': 0, 'dayOfMonth': 0, 'firstDayOfWeek': 'sunday', 'index': 'first'}, 'range': {'type': 'endDate', 'startDate': '2020-05-03', 'endDate': '2020-05-05', 'recurrenceTimeZone': 'Romance Standard Time', 'numberOfOccurrences': 0}}, 'attendees': [], 'organizer': {'emailAddress': {'name': 'outlook_7BA43549E5FD4413@outlook.com', 'address': 'outlook_7BA43549E5FD4413@outlook.com'}}},
|
||||
@@ -65,6 +70,7 @@ class TestSyncMicrosoft2Flectra(TransactionCase):
|
||||
self.assertEqual(recurrence.base_event_id, events[0])
|
||||
self.assertEqual(events.mapped('name'), ['My recurrent event', 'My recurrent event'])
|
||||
|
||||
@freeze_time('2020-06-03')
|
||||
def test_microsoft_recurrence_change_name_one_event(self):
|
||||
values = [
|
||||
{'@odata.type': '#microsoft.graph.event', '@odata.etag': 'W/"ZS2uEVAVyU6BMZ3m6cHmtgAADIaZKQ=="', 'createdDateTime': '2020-05-06T07:03:49.1444085Z', 'lastModifiedDateTime': self.datetime_future, 'changeKey': 'ZS2uEVAVyU6BMZ3m6cHmtgAADIaZKQ==', 'categories': [], 'originalStartTimeZone': 'Romance Standard Time', 'originalEndTimeZone': 'Romance Standard Time', 'iCalUId': '040000008200E00074C5B7101A82E00800000000874F057E7423D601000000000000000010000000C6918C4B44D2D84586351FEC8B1B7F8C', 'reminderMinutesBeforeStart': 15, 'isReminderOn': True, 'hasAttachments': False, 'subject': 'My recurrent event', 'bodyPreview': '', 'importance': 'normal', 'sensitivity': 'normal', 'isAllDay': False, 'isCancelled': False, 'isOrganizer': True, 'IsRoomRequested': False, 'AutoRoomBookingStatus': 'None', 'responseRequested': True, 'seriesMasterId': None, 'showAs': 'busy', 'type': 'seriesMaster', 'webLink': 'https://outlook.live.com/owa/?itemid=AQ8PojGtrADQATM3ZmYAZS0yY2MAMC00MDg1LTAwAi0wMAoARgAAA0By7X03vaNKv1GnWYTbFYAHAGUtrhFQFclOgTGd5unB5rYAAAIBDQAAAGUtrhFQFclOgTGd5unB5rYAAAALLLTEAAAA&exvsurl=1&path=/calendar/item', 'onlineMeetingUrl': None, 'isOnlineMeeting': False, 'onlineMeetingProvider': 'unknown', 'AllowNewTimeProposals': True, 'IsDraft': False, 'id': 'AQ8PojGtrADQATM3ZmYAZS0yY2MAMC00MDg1LTAwAi0wMAoARgAAA0By7X03vaNKv1GnWYTbFYAHAGUtrhFQFclOgTGd5unB5rYAAAIBDQAAAGUtrhFQFclOgTGd5unB5rYAAAALLLTEAAAA', 'responseStatus': {'response': 'organizer', 'time': '0001-01-01T00:00:00Z'}, 'body': {'contentType': 'html', 'content': ''}, 'start': {'dateTime': '2020-05-03T14:30:00.0000000', 'timeZone': 'UTC'}, 'end': {'dateTime': '2020-05-03T16:00:00.0000000', 'timeZone': 'UTC'}, 'location': {'displayName': '', 'locationType': 'default', 'uniqueIdType': 'unknown', 'address': {}, 'coordinates': {}}, 'locations': [], 'recurrence': {'pattern': {'type': 'daily', 'interval': 1, 'month': 0, 'dayOfMonth': 0, 'firstDayOfWeek': 'sunday', 'index': 'first'}, 'range': {'type': 'endDate', 'startDate': '2020-05-03', 'endDate': '2020-05-05', 'recurrenceTimeZone': 'Romance Standard Time', 'numberOfOccurrences': 0}}, 'attendees': [], 'organizer': {'emailAddress': {'name': 'outlook_7BA43549E5FD4413@outlook.com', 'address': 'outlook_7BA43549E5FD4413@outlook.com'}}},
|
||||
@@ -82,6 +88,7 @@ class TestSyncMicrosoft2Flectra(TransactionCase):
|
||||
self.assertEqual(recurrence.base_event_id, events[0])
|
||||
self.assertEqual(events.mapped('name'), ['My recurrent event', 'My recurrent event 2', 'My recurrent event'])
|
||||
|
||||
@freeze_time('2020-06-03')
|
||||
def test_microsoft_recurrence_change_name_all_event(self):
|
||||
values = [
|
||||
{'@odata.type': '#microsoft.graph.event', '@odata.etag': 'W/"ZS2uEVAVyU6BMZ3m6cHmtgAADIaZKQ=="', 'createdDateTime': '2020-05-06T07:03:49.1444085Z', 'lastModifiedDateTime': self.datetime_future, 'changeKey': 'ZS2uEVAVyU6BMZ3m6cHmtgAADIaZKQ==', 'categories': [], 'originalStartTimeZone': 'Romance Standard Time', 'originalEndTimeZone': 'Romance Standard Time', 'iCalUId': '040000008200E00074C5B7101A82E00800000000874F057E7423D601000000000000000010000000C6918C4B44D2D84586351FEC8B1B7F8C', 'reminderMinutesBeforeStart': 15, 'isReminderOn': True, 'hasAttachments': False, 'subject': 'My recurrent event 2', 'bodyPreview': '', 'importance': 'normal', 'sensitivity': 'normal', 'isAllDay': False, 'isCancelled': False, 'isOrganizer': True, 'IsRoomRequested': False, 'AutoRoomBookingStatus': 'None', 'responseRequested': True, 'seriesMasterId': None, 'showAs': 'busy', 'type': 'seriesMaster', 'webLink': 'https://outlook.live.com/owa/?itemid=AQ8PojGtrADQATM3ZmYAZS0yY2MAMC00MDg1LTAwAi0wMAoARgAAA0By7X03vaNKv1GnWYTbFYAHAGUtrhFQFclOgTGd5unB5rYAAAIBDQAAAGUtrhFQFclOgTGd5unB5rYAAAALLLTEAAAA&exvsurl=1&path=/calendar/item', 'onlineMeetingUrl': None, 'isOnlineMeeting': False, 'onlineMeetingProvider': 'unknown', 'AllowNewTimeProposals': True, 'IsDraft': False, 'id': 'AQ8PojGtrADQATM3ZmYAZS0yY2MAMC00MDg1LTAwAi0wMAoARgAAA0By7X03vaNKv1GnWYTbFYAHAGUtrhFQFclOgTGd5unB5rYAAAIBDQAAAGUtrhFQFclOgTGd5unB5rYAAAALLLTEAAAA', 'responseStatus': {'response': 'organizer', 'time': '0001-01-01T00:00:00Z'}, 'body': {'contentType': 'html', 'content': ''}, 'start': {'dateTime': '2020-05-03T14:30:00.0000000', 'timeZone': 'UTC'}, 'end': {'dateTime': '2020-05-03T16:00:00.0000000', 'timeZone': 'UTC'}, 'location': {'displayName': '', 'locationType': 'default', 'uniqueIdType': 'unknown', 'address': {}, 'coordinates': {}}, 'locations': [], 'recurrence': {'pattern': {'type': 'daily', 'interval': 1, 'month': 0, 'dayOfMonth': 0, 'firstDayOfWeek': 'sunday', 'index': 'first'}, 'range': {'type': 'endDate', 'startDate': '2020-05-03', 'endDate': '2020-05-05', 'recurrenceTimeZone': 'Romance Standard Time', 'numberOfOccurrences': 0}}, 'attendees': [], 'organizer': {'emailAddress': {'name': 'outlook_7BA43549E5FD4413@outlook.com', 'address': 'outlook_7BA43549E5FD4413@outlook.com'}}},
|
||||
@@ -99,6 +106,7 @@ class TestSyncMicrosoft2Flectra(TransactionCase):
|
||||
self.assertEqual(recurrence.base_event_id, events[0])
|
||||
self.assertEqual(events.mapped('name'), ['My recurrent event 2', 'My recurrent event 2', 'My recurrent event 2'])
|
||||
|
||||
@freeze_time('2020-06-03')
|
||||
def test_microsoft_recurrence_change_date_one_event(self):
|
||||
values = [
|
||||
{'@odata.type': '#microsoft.graph.event', '@odata.etag': 'W/"ZS2uEVAVyU6BMZ3m6cHmtgAADIaZPA=="', 'createdDateTime': '2020-05-06T07:03:49.1444085Z', 'lastModifiedDateTime': self.datetime_future, 'changeKey': 'ZS2uEVAVyU6BMZ3m6cHmtgAADIaZPA==', 'categories': [], 'originalStartTimeZone': 'Romance Standard Time', 'originalEndTimeZone': 'Romance Standard Time', 'iCalUId': '040000008200E00074C5B7101A82E00800000000874F057E7423D601000000000000000010000000C6918C4B44D2D84586351FEC8B1B7F8C', 'reminderMinutesBeforeStart': 15, 'isReminderOn': True, 'hasAttachments': False, 'subject': 'My recurrent event', 'bodyPreview': '', 'importance': 'normal', 'sensitivity': 'normal', 'isAllDay': False, 'isCancelled': False, 'isOrganizer': True, 'IsRoomRequested': False, 'AutoRoomBookingStatus': 'None', 'responseRequested': True, 'seriesMasterId': None, 'showAs': 'busy', 'type': 'seriesMaster', 'webLink': 'https://outlook.live.com/owa/?itemid=AQ8PojGtrADQATM3ZmYAZS0yY2MAMC00MDg1LTAwAi0wMAoARgAAA0By7X03vaNKv1GnWYTbFYAHAGUtrhFQFclOgTGd5unB5rYAAAIBDQAAAGUtrhFQFclOgTGd5unB5rYAAAALLLTEAAAA&exvsurl=1&path=/calendar/item', 'onlineMeetingUrl': None, 'isOnlineMeeting': False, 'onlineMeetingProvider': 'unknown', 'AllowNewTimeProposals': True, 'IsDraft': False, 'id': 'AQ8PojGtrADQATM3ZmYAZS0yY2MAMC00MDg1LTAwAi0wMAoARgAAA0By7X03vaNKv1GnWYTbFYAHAGUtrhFQFclOgTGd5unB5rYAAAIBDQAAAGUtrhFQFclOgTGd5unB5rYAAAALLLTEAAAA', 'responseStatus': {'response': 'organizer', 'time': '0001-01-01T00:00:00Z'}, 'body': {'contentType': 'html', 'content': ''}, 'start': {'dateTime': '2020-05-03T14:30:00.0000000', 'timeZone': 'UTC'}, 'end': {'dateTime': '2020-05-03T16:00:00.0000000', 'timeZone': 'UTC'}, 'location': {'displayName': '', 'locationType': 'default', 'uniqueIdType': 'unknown', 'address': {}, 'coordinates': {}}, 'locations': [], 'recurrence': {'pattern': {'type': 'daily', 'interval': 1, 'month': 0, 'dayOfMonth': 0, 'firstDayOfWeek': 'sunday', 'index': 'first'}, 'range': {'type': 'endDate', 'startDate': '2020-05-03', 'endDate': '2020-05-05', 'recurrenceTimeZone': 'Romance Standard Time', 'numberOfOccurrences': 0}}, 'attendees': [], 'organizer': {'emailAddress': {'name': 'outlook_7BA43549E5FD4413@outlook.com', 'address': 'outlook_7BA43549E5FD4413@outlook.com'}}},
|
||||
@@ -126,6 +134,7 @@ class TestSyncMicrosoft2Flectra(TransactionCase):
|
||||
self.assertEqual(special_event.start, datetime(2020, 5, 4, 14, 30))
|
||||
self.assertEqual(special_event.stop, datetime(2020, 5, 4, 17, 00))
|
||||
|
||||
@freeze_time('2020-06-03')
|
||||
def test_microsoft_recurrence_delete_first_event(self):
|
||||
values = [
|
||||
{'@odata.type': '#microsoft.graph.event', '@odata.etag': 'W/"ZS2uEVAVyU6BMZ3m6cHmtgAADI/Bnw=="', 'createdDateTime': '2020-05-06T07:03:49.1444085Z', 'lastModifiedDateTime': self.datetime_future, 'changeKey': 'ZS2uEVAVyU6BMZ3m6cHmtgAADI/Bnw==', 'categories': [], 'originalStartTimeZone': 'Romance Standard Time', 'originalEndTimeZone': 'Romance Standard Time', 'iCalUId': '040000008200E00074C5B7101A82E00800000000874F057E7423D601000000000000000010000000C6918C4B44D2D84586351FEC8B1B7F8C', 'reminderMinutesBeforeStart': 15, 'isReminderOn': True, 'hasAttachments': False, 'subject': 'My recurrent event', 'bodyPreview': '', 'importance': 'normal', 'sensitivity': 'normal', 'isAllDay': False, 'isCancelled': False, 'isOrganizer': True, 'IsRoomRequested': False, 'AutoRoomBookingStatus': 'None', 'responseRequested': True, 'seriesMasterId': None, 'showAs': 'busy', 'type': 'seriesMaster', 'webLink': 'https://outlook.live.com/owa/?itemid=AQ8PojGtrADQATM3ZmYAZS0yY2MAMC00MDg1LTAwAi0wMAoARgAAA0By7X03vaNKv1GnWYTbFYAHAGUtrhFQFclOgTGd5unB5rYAAAIBDQAAAGUtrhFQFclOgTGd5unB5rYAAAALLLTEAAAA&exvsurl=1&path=/calendar/item', 'onlineMeetingUrl': None, 'isOnlineMeeting': False, 'onlineMeetingProvider': 'unknown', 'AllowNewTimeProposals': True, 'IsDraft': False, 'id': 'AQ8PojGtrADQATM3ZmYAZS0yY2MAMC00MDg1LTAwAi0wMAoARgAAA0By7X03vaNKv1GnWYTbFYAHAGUtrhFQFclOgTGd5unB5rYAAAIBDQAAAGUtrhFQFclOgTGd5unB5rYAAAALLLTEAAAA', 'responseStatus': {'response': 'organizer', 'time': '0001-01-01T00:00:00Z'}, 'body': {'contentType': 'html', 'content': ''}, 'start': {'dateTime': '2020-05-03T14:30:00.0000000', 'timeZone': 'UTC'}, 'end': {'dateTime': '2020-05-03T16:00:00.0000000', 'timeZone': 'UTC'}, 'location': {'displayName': '', 'locationType': 'default', 'uniqueIdType': 'unknown', 'address': {}, 'coordinates': {}}, 'locations': [], 'recurrence': {'pattern': {'type': 'daily', 'interval': 1, 'month': 0, 'dayOfMonth': 0, 'firstDayOfWeek': 'sunday', 'index': 'first'}, 'range': {'type': 'endDate', 'startDate': '2020-05-03', 'endDate': '2020-05-05', 'recurrenceTimeZone': 'Romance Standard Time', 'numberOfOccurrences': 0}}, 'attendees': [], 'organizer': {'emailAddress': {'name': 'outlook_7BA43549E5FD4413@outlook.com', 'address': 'outlook_7BA43549E5FD4413@outlook.com'}}},
|
||||
@@ -178,6 +187,7 @@ class TestSyncMicrosoft2Flectra(TransactionCase):
|
||||
self.assertEqual(events[2].start, datetime(2020, 5, 5, 14, 30))
|
||||
self.assertEqual(events[2].stop, datetime(2020, 5, 5, 16, 30))
|
||||
|
||||
@freeze_time('2020-06-03')
|
||||
def test_microsoft_recurrence_split_recurrence(self):
|
||||
values = [
|
||||
{'@odata.type': '#microsoft.graph.event', '@odata.etag': 'W/"ZS2uEVAVyU6BMZ3m6cHmtgAADI/Dig=="', 'createdDateTime': '2020-05-06T07:03:49.1444085Z', 'lastModifiedDateTime': self.datetime_future, 'changeKey': 'ZS2uEVAVyU6BMZ3m6cHmtgAADI/Dig==', 'categories': [], 'originalStartTimeZone': 'Romance Standard Time', 'originalEndTimeZone': 'Romance Standard Time', 'iCalUId': '040000008200E00074C5B7101A82E00800000000874F057E7423D601000000000000000010000000C6918C4B44D2D84586351FEC8B1B7F8C', 'reminderMinutesBeforeStart': 15, 'isReminderOn': True, 'hasAttachments': False, 'subject': 'My recurrent event', 'bodyPreview': '', 'importance': 'normal', 'sensitivity': 'normal', 'isAllDay': False, 'isCancelled': False, 'isOrganizer': True, 'IsRoomRequested': False, 'AutoRoomBookingStatus': 'None', 'responseRequested': True, 'seriesMasterId': None, 'showAs': 'busy', 'type': 'seriesMaster', 'webLink': 'https://outlook.live.com/owa/?itemid=AQ8PojGtrADQATM3ZmYAZS0yY2MAMC00MDg1LTAwAi0wMAoARgAAA0By7X03vaNKv1GnWYTbFYAHAGUtrhFQFclOgTGd5unB5rYAAAIBDQAAAGUtrhFQFclOgTGd5unB5rYAAAALLLTEAAAA&exvsurl=1&path=/calendar/item', 'onlineMeetingUrl': None, 'isOnlineMeeting': False, 'onlineMeetingProvider': 'unknown', 'AllowNewTimeProposals': True, 'IsDraft': False, 'id': 'AQ8PojGtrADQATM3ZmYAZS0yY2MAMC00MDg1LTAwAi0wMAoARgAAA0By7X03vaNKv1GnWYTbFYAHAGUtrhFQFclOgTGd5unB5rYAAAIBDQAAAGUtrhFQFclOgTGd5unB5rYAAAALLLTEAAAA', 'responseStatus': {'response': 'organizer', 'time': '0001-01-01T00:00:00Z'}, 'body': {'contentType': 'html', 'content': ''}, 'start': {'dateTime': '2020-05-03T14:30:00.0000000', 'timeZone': 'UTC'}, 'end': {'dateTime': '2020-05-03T16:30:00.0000000', 'timeZone': 'UTC'}, 'location': {'displayName': '', 'locationType': 'default', 'uniqueIdType': 'unknown', 'address': {}, 'coordinates': {}}, 'locations': [], 'recurrence': {'pattern': {'type': 'daily', 'interval': 1, 'month': 0, 'dayOfMonth': 0, 'firstDayOfWeek': 'sunday', 'index': 'first'}, 'range': {'type': 'endDate', 'startDate': '2020-05-03', 'endDate': '2020-05-03', 'recurrenceTimeZone': 'Romance Standard Time', 'numberOfOccurrences': 0}}, 'attendees': [], 'organizer': {'emailAddress': {'name': 'outlook_7BA43549E5FD4413@outlook.com', 'address': 'outlook_7BA43549E5FD4413@outlook.com'}}},
|
||||
@@ -211,6 +221,7 @@ class TestSyncMicrosoft2Flectra(TransactionCase):
|
||||
self.assertEqual(events_2[2].start, datetime(2020, 5, 6, 14, 30))
|
||||
self.assertEqual(events_2[2].stop, datetime(2020, 5, 6, 17, 00))
|
||||
|
||||
@freeze_time('2020-06-03')
|
||||
def test_microsoft_recurrence_delete(self):
|
||||
recurrence_id = self.env['calendar.recurrence'].search([('microsoft_id', '=', self.recurrence_id)])
|
||||
event_ids = self.env['calendar.event'].search([('recurrence_id', '=', recurrence_id.id)], order='start asc').ids
|
||||
@@ -223,6 +234,7 @@ class TestSyncMicrosoft2Flectra(TransactionCase):
|
||||
self.assertFalse(recurrence, "It should remove recurrence")
|
||||
self.assertFalse(events, "It should remove all events")
|
||||
|
||||
@freeze_time('2020-06-03')
|
||||
def test_attendees_must_have_email(self):
|
||||
"""
|
||||
Synching with a partner without mail raises a ValidationError because Microsoft don't accept attendees without one.
|
||||
@@ -240,6 +252,7 @@ class TestSyncMicrosoft2Flectra(TransactionCase):
|
||||
with self.assertRaises(ValidationError):
|
||||
event._sync_flectra2microsoft(MicrosoftCal)
|
||||
|
||||
@freeze_time('2020-06-03')
|
||||
def test_cancel_occurence_of_recurrent_event(self):
|
||||
""" The user is invited to a recurrent event. When synced, all events are present, there are three occurrences:
|
||||
- 07/15/2021, 15:00-15:30
|
||||
|
||||
@@ -202,6 +202,9 @@ class StockMove(models.Model):
|
||||
defaults['state'] = 'done'
|
||||
defaults['product_uom_qty'] = 0.0
|
||||
defaults['additional'] = True
|
||||
elif production_id.state == 'draft':
|
||||
defaults['group_id'] = production_id.procurement_group_id.id
|
||||
defaults['reference'] = production_id.name
|
||||
return defaults
|
||||
|
||||
def write(self, vals):
|
||||
|
||||
@@ -505,3 +505,32 @@ class TestMultistepManufacturingWarehouse(TestMrpCommon):
|
||||
self.assertEqual(finished_product_SML.product_uom_qty, 2)
|
||||
self.assertEqual(secondary_product_SML.product_uom.id, four_units_uom.id)
|
||||
self.assertEqual(secondary_product_SML.product_uom_qty, 1)
|
||||
|
||||
def test_2_steps_and_additional_moves(self):
|
||||
""" Suppose a 2-steps configuration. If a user adds a product to an existing draft MO and then
|
||||
confirms it, the associated picking should includes this new product"""
|
||||
self.warehouse.manufacture_steps = 'pbm'
|
||||
|
||||
mo_form = Form(self.env['mrp.production'])
|
||||
mo_form.product_id = self.bom.product_id
|
||||
mo_form.picking_type_id = self.warehouse.manu_type_id
|
||||
mo = mo_form.save()
|
||||
|
||||
component_move = mo.move_raw_ids[0]
|
||||
mo.with_context(default_raw_material_production_id=mo.id).move_raw_ids = [
|
||||
[0, 0, {
|
||||
'location_id': component_move.location_id.id,
|
||||
'location_dest_id': component_move.location_dest_id.id,
|
||||
'picking_type_id': component_move.picking_type_id.id,
|
||||
'product_id': self.product_2.id,
|
||||
'name': self.product_2.display_name,
|
||||
'product_uom_qty': 1,
|
||||
'product_uom': self.product_2.uom_id.id,
|
||||
'warehouse_id': component_move.warehouse_id.id,
|
||||
'raw_material_production_id': mo.id,
|
||||
}]
|
||||
]
|
||||
|
||||
mo.action_confirm()
|
||||
|
||||
self.assertEqual(self.bom.bom_line_ids.product_id + self.product_2, mo.picking_ids.move_lines.product_id)
|
||||
@@ -271,7 +271,9 @@
|
||||
<field name="show_details_visible" invisible="1"/>
|
||||
<field name="lot_ids" widget="many2many_tags"
|
||||
groups="stock.group_production_lot" optional="hide"
|
||||
attrs="{'invisible': ['|', '|', ('show_details_visible', '=', False), ('has_tracking', '!=', 'serial'), ('parent.state', '=', 'draft')], 'column_invisible': [('parent.show_lot_ids', '=', False)]}"
|
||||
attrs="{'invisible': ['|', '|', ('show_details_visible', '=', False), ('has_tracking', '!=', 'serial'), ('parent.state', '=', 'draft')],
|
||||
'readonly': ['|', '|', ('show_details_visible', '=', False), ('has_tracking', '!=', 'serial'), ('parent.state', '=', 'draft')],
|
||||
'column_invisible': [('parent.show_lot_ids', '=', False)]}"
|
||||
options="{'create': [('parent.use_create_components_lots', '!=', False)]}"
|
||||
context="{'default_company_id': company_id, 'default_product_id': product_id}"
|
||||
domain="[('product_id','=',product_id)]"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo, Flectra. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from flectra import api, fields, models, _
|
||||
from flectra import api, fields, models, SUPERUSER_ID, _
|
||||
from flectra.exceptions import UserError
|
||||
|
||||
|
||||
@@ -70,6 +70,7 @@ class ResConfigSettings(models.TransientModel):
|
||||
raise UserError(_("You can't desactivate the multi-location if you have more than once warehouse by company"))
|
||||
|
||||
previous_group = self.default_get(['group_stock_multi_locations', 'group_stock_production_lot', 'group_stock_tracking_lot'])
|
||||
was_operations_showed = self.env['stock.picking.type'].with_user(SUPERUSER_ID)._default_show_operations()
|
||||
res = super(ResConfigSettings, self).set_values()
|
||||
|
||||
if not self.user_has_groups('stock.group_stock_manager'):
|
||||
@@ -89,7 +90,7 @@ class ResConfigSettings(models.TransientModel):
|
||||
('delivery_steps', '=', 'ship_only')]
|
||||
).mapped('int_type_id').write({'active': False})
|
||||
|
||||
if any(self[group] and not prev_value for group, prev_value in previous_group.items()):
|
||||
if not was_operations_showed and self.env['stock.picking.type'].with_user(SUPERUSER_ID)._default_show_operations():
|
||||
picking_types = self.env['stock.picking.type'].with_context(active_test=False).search([
|
||||
('code', '!=', 'incoming'),
|
||||
('show_operations', '=', False)
|
||||
|
||||
@@ -452,7 +452,8 @@ class InventoryLine(models.Model):
|
||||
set in an onchange of `product_id`.
|
||||
Finally, this override checks we don't try to create a duplicated line.
|
||||
"""
|
||||
for values in vals_list:
|
||||
products = self.env['product.product'].browse([vals.get('product_id') for vals in vals_list])
|
||||
for product, values in zip(products, vals_list):
|
||||
if 'theoretical_qty' not in values:
|
||||
theoretical_qty = self.env['product.product'].get_theoretical_quantity(
|
||||
values['product_id'],
|
||||
@@ -464,7 +465,7 @@ class InventoryLine(models.Model):
|
||||
)
|
||||
values['theoretical_qty'] = theoretical_qty
|
||||
if 'product_id' in values and 'product_uom_id' not in values:
|
||||
values['product_uom_id'] = self.env['product.product'].browse(values['product_id']).uom_id.id
|
||||
values['product_uom_id'] = product.product_tmpl_id.uom_id.id
|
||||
res = super(InventoryLine, self).create(vals_list)
|
||||
res._check_no_duplicate_line()
|
||||
return res
|
||||
@@ -475,17 +476,16 @@ class InventoryLine(models.Model):
|
||||
return res
|
||||
|
||||
def _check_no_duplicate_line(self):
|
||||
# Performance: restrict domain on 'not null' fields of stock_inventory_line.
|
||||
domain = [('product_id', 'in', self.product_id.ids), ('location_id', 'in', self.location_id.ids)]
|
||||
groupby_fields = ['product_id', 'location_id', 'partner_id', 'package_id', 'prod_lot_id', 'inventory_id']
|
||||
lines_count = {}
|
||||
for group in self.read_group(domain, ['product_id'], groupby_fields, lazy=False):
|
||||
key = tuple([group[field] and group[field][0] for field in groupby_fields])
|
||||
lines_count[key] = group['__count']
|
||||
for line in self:
|
||||
domain = [
|
||||
('id', '!=', line.id),
|
||||
('product_id', '=', line.product_id.id),
|
||||
('location_id', '=', line.location_id.id),
|
||||
('partner_id', '=', line.partner_id.id),
|
||||
('package_id', '=', line.package_id.id),
|
||||
('prod_lot_id', '=', line.prod_lot_id.id),
|
||||
('inventory_id', '=', line.inventory_id.id)]
|
||||
existings = self.search_count(domain)
|
||||
if existings:
|
||||
key = (line.product_id.id, line.location_id.id, line.partner_id.id, line.package_id.id, line.prod_lot_id.id, line.inventory_id.id)
|
||||
if lines_count[key] > 1:
|
||||
raise UserError(_("There is already one inventory adjustment line for this product,"
|
||||
" you should rather modify this one instead of creating a new one."))
|
||||
|
||||
|
||||
@@ -1089,6 +1089,8 @@ class Picking(models.Model):
|
||||
moves_to_backorder.mapped('package_level_id').write({'picking_id':backorder_picking.id})
|
||||
moves_to_backorder.mapped('move_line_ids').write({'picking_id': backorder_picking.id})
|
||||
backorders |= backorder_picking
|
||||
if backorders:
|
||||
backorders.action_assign()
|
||||
return backorders
|
||||
|
||||
def _log_activity_get_documents(self, orig_obj_changes, stream_field, stream, sorted_method=False, groupby_method=False):
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo, Flectra. See LICENSE file for full copyright and licensing details.
|
||||
from flectra.exceptions import ValidationError
|
||||
from flectra.exceptions import ValidationError, UserError
|
||||
from flectra.tests.common import Form, SavepointCase
|
||||
|
||||
|
||||
@@ -802,3 +802,24 @@ class TestInventory(SavepointCase):
|
||||
self.assertEqual(len(line_ids_loc2), 1)
|
||||
self.assertEqual(line_ids_loc1[0].theoretical_qty, 10)
|
||||
self.assertEqual(line_ids_loc2[0].theoretical_qty, 0)
|
||||
|
||||
def test_inventory_line_duplicates(self):
|
||||
""" Checks that creating duplicated inventory lines
|
||||
raises a UserError.
|
||||
"""
|
||||
inventory = self.env['stock.inventory'].create({
|
||||
'name': 'Existing Inventory',
|
||||
'exhausted': True,
|
||||
'location_ids': [(4, self.stock_location.id)],
|
||||
'product_ids': [(4, self.product1.id)]
|
||||
})
|
||||
inventory.action_start()
|
||||
|
||||
dup_vals = [{
|
||||
'inventory_id': inventory.id,
|
||||
'product_id': self.product1.id,
|
||||
'location_id': self.stock_location.id,
|
||||
}]
|
||||
|
||||
with self.assertRaises(UserError), self.cr.savepoint():
|
||||
self.env['stock.inventory.line'].create(dup_vals)
|
||||
@@ -122,7 +122,7 @@ class TestPickShip(TestStockCommon):
|
||||
picking_client._action_done() # no new in order to create backorder
|
||||
|
||||
backorder = self.env['stock.picking'].search([('backorder_id', '=', picking_client.id)])
|
||||
self.assertEqual(backorder.state, 'waiting', 'Backorder should be waiting for reservation')
|
||||
self.assertEqual(backorder.state, 'assigned', 'Backorder should be started')
|
||||
|
||||
def test_mto_moves_transfer(self):
|
||||
"""
|
||||
@@ -293,7 +293,7 @@ class TestPickShip(TestStockCommon):
|
||||
# create a backorder
|
||||
picking_pick._action_done()
|
||||
picking_pick_backorder = self.env['stock.picking'].search([('backorder_id', '=', picking_pick.id)])
|
||||
self.assertEqual(picking_pick_backorder.state, 'confirmed')
|
||||
self.assertEqual(picking_pick_backorder.state, 'assigned')
|
||||
self.assertEqual(picking_pick_backorder.move_lines.product_qty, 5.0)
|
||||
|
||||
self.assertEqual(picking_client.state, 'assigned')
|
||||
@@ -773,11 +773,8 @@ class TestSinglePicking(TestStockCommon):
|
||||
delivery_order.move_lines[0].move_line_ids[0].qty_done = 1
|
||||
delivery_order._action_done()
|
||||
self.assertNotEqual(delivery_order.date_done, False)
|
||||
self.assertEqual(self.env['stock.quant']._get_available_quantity(self.productA, pack_location), 1.0)
|
||||
|
||||
backorder = self.env['stock.picking'].search([('backorder_id', '=', delivery_order.id)])
|
||||
self.assertEqual(backorder.state, 'confirmed')
|
||||
backorder.action_assign()
|
||||
self.assertEqual(backorder.state, 'assigned')
|
||||
self.assertEqual(self.env['stock.quant']._get_available_quantity(self.productA, pack_location), 0.0)
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ class StockQuant(models.Model):
|
||||
quant.value = 0
|
||||
continue
|
||||
if quant.product_id.cost_method == 'fifo':
|
||||
quantity = quant.product_id.quantity_svl
|
||||
quantity = quant.product_id.with_company(quant.company_id).quantity_svl
|
||||
if float_is_zero(quantity, precision_rounding=quant.product_id.uom_id.rounding):
|
||||
quant.value = 0.0
|
||||
continue
|
||||
|
||||
@@ -2047,6 +2047,9 @@ var SnippetsMenu = Widget.extend({
|
||||
return dragSnip;
|
||||
},
|
||||
start: function () {
|
||||
const prom = new Promise(resolve => dragAndDropResolve = () => resolve());
|
||||
self._mutex.exec(() => prom);
|
||||
|
||||
self.$el.find('.oe_snippet_thumbnail').addClass('o_we_already_dragging');
|
||||
|
||||
dropped = false;
|
||||
@@ -2112,9 +2115,6 @@ var SnippetsMenu = Widget.extend({
|
||||
self.draggableComponent.$scrollTarget.on('scroll.scrolling_element', function () {
|
||||
self.$el.trigger('scroll');
|
||||
});
|
||||
|
||||
const prom = new Promise(resolve => dragAndDropResolve = () => resolve());
|
||||
self._mutex.exec(() => prom);
|
||||
},
|
||||
stop: async function (ev, ui) {
|
||||
$toInsert.removeClass('oe_snippet_body');
|
||||
|
||||
@@ -682,6 +682,18 @@ registry.backgroundVideo = publicWidget.Widget.extend({
|
||||
|
||||
$(window).on('resize.' + this.iframeID, throttledUpdate);
|
||||
|
||||
const $modal = this.$target.closest('.modal');
|
||||
if ($modal.length) {
|
||||
$modal.on('show.bs.modal', () => {
|
||||
const videoContainerEl = this.$target[0].querySelector('.o_bg_video_container');
|
||||
videoContainerEl.classList.add('d-none');
|
||||
});
|
||||
$modal.on('shown.bs.modal', () => {
|
||||
this._adjustIframe();
|
||||
const videoContainerEl = this.$target[0].querySelector('.o_bg_video_container');
|
||||
videoContainerEl.classList.remove('d-none');
|
||||
});
|
||||
}
|
||||
return Promise.all(proms).then(() => this._appendBgVideo());
|
||||
},
|
||||
/**
|
||||
|
||||
@@ -10,6 +10,7 @@ const PopupWidget = publicWidget.Widget.extend({
|
||||
events: {
|
||||
'click .js_close_popup': '_onCloseClick',
|
||||
'hide.bs.modal': '_onHideModal',
|
||||
'show.bs.modal': '_onShowModal',
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -92,6 +93,19 @@ const PopupWidget = publicWidget.Widget.extend({
|
||||
const nbDays = this.$el.find('.modal').data('consentsDuration');
|
||||
utils.set_cookie(this.$el.attr('id'), true, nbDays * 24 * 60 * 60);
|
||||
this._popupAlreadyShown = true;
|
||||
|
||||
this.$target.find('.media_iframe_video iframe').each((i, iframe) => {
|
||||
iframe.src = '';
|
||||
});
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_onShowModal() {
|
||||
this.el.querySelectorAll('.media_iframe_video').forEach(media => {
|
||||
const iframe = media.querySelector('iframe');
|
||||
iframe.src = media.dataset.oeExpression || media.dataset.src; // TODO still oeExpression to remove someday
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -17,10 +17,21 @@ options.registry.SnippetPopup = options.Class.extend({
|
||||
});
|
||||
this.$target.on('shown.bs.modal.SnippetPopup', () => {
|
||||
this.trigger_up('snippet_option_visibility_update', {show: true});
|
||||
// TODO duplicated code from the popup public widget, this should
|
||||
// be moved to a *video* public widget and be reviewed in master
|
||||
this.$target[0].querySelectorAll('.media_iframe_video').forEach(media => {
|
||||
const iframe = media.querySelector('iframe');
|
||||
iframe.src = media.dataset.oeExpression || media.dataset.src; // TODO still oeExpression to remove someday
|
||||
});
|
||||
});
|
||||
this.$target.on('hidden.bs.modal.SnippetPopup', () => {
|
||||
this.trigger_up('snippet_option_visibility_update', {show: false});
|
||||
this._removeIframeSrc();
|
||||
});
|
||||
// The video might be playing before entering edit mode (possibly with
|
||||
// sound). Stop the video, as the user can't do it (no button on video
|
||||
// in edit mode).
|
||||
this._removeIframeSrc();
|
||||
return this._super(...arguments);
|
||||
},
|
||||
/**
|
||||
@@ -28,6 +39,9 @@ options.registry.SnippetPopup = options.Class.extend({
|
||||
*/
|
||||
destroy: function () {
|
||||
this._super(...arguments);
|
||||
// The video should not start before the modal opens, remove it from the
|
||||
// DOM. It will be added back on modal open to start the video.
|
||||
this._removeIframeSrc();
|
||||
this.$target.off('.SnippetPopup');
|
||||
},
|
||||
/**
|
||||
@@ -110,5 +124,16 @@ options.registry.SnippetPopup = options.Class.extend({
|
||||
}
|
||||
return this._super(...arguments);
|
||||
},
|
||||
/**
|
||||
* Removes the iframe `src` attribute (a copy of the src is already on the
|
||||
* parent `oe-expression` attribute).
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_removeIframeSrc() {
|
||||
this.$target.find('.media_iframe_video iframe').each((i, iframe) => {
|
||||
iframe.src = '';
|
||||
});
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -10,14 +10,17 @@ const ShareWidget = publicWidget.Widget.extend({
|
||||
* @override
|
||||
*/
|
||||
start: function () {
|
||||
var urlRegex = /(\?(?:|.*&)(?:u|url|body)=)(.*?)(&|#|$)/;
|
||||
var titleRegex = /(\?(?:|.*&)(?:title|text|subject)=)(.*?)(&|#|$)/;
|
||||
var url = encodeURIComponent(window.location.href);
|
||||
var title = encodeURIComponent($('title').text());
|
||||
this.$('a').each(function () {
|
||||
var $a = $(this);
|
||||
$a.attr('href', function (i, href) {
|
||||
return href.replace(urlRegex, function (match, a, b, c) {
|
||||
const urlRegex = /(\?(?:|.*&)(?:u|url|body)=)(.*?)(&|#|$)/;
|
||||
const titleRegex = /(\?(?:|.*&)(?:title|text|subject|description)=)(.*?)(&|#|$)/;
|
||||
const mediaRegex = /(\?(?:|.*&)(?:media)=)(.*?)(&|#|$)/;
|
||||
const url = encodeURIComponent(window.location.href);
|
||||
const title = encodeURIComponent($('title').text());
|
||||
const media = encodeURIComponent($('meta[property="og:image"]').attr('content'));
|
||||
|
||||
this.$('a').each((index, element) => {
|
||||
const $a = $(element);
|
||||
$a.attr('href', (i, href) => {
|
||||
return href.replace(urlRegex, (match, a, b, c) => {
|
||||
return a + url + c;
|
||||
}).replace(titleRegex, function (match, a, b, c) {
|
||||
if ($a.hasClass('s_share_whatsapp')) {
|
||||
@@ -29,6 +32,8 @@ const ShareWidget = publicWidget.Widget.extend({
|
||||
return a + title + url + c;
|
||||
}
|
||||
return a + title + c;
|
||||
}).replace(mediaRegex, (match, a, b, c) => {
|
||||
return a + media + c;
|
||||
});
|
||||
});
|
||||
if ($a.attr('target') && $a.attr('target').match(/_blank/i) && !$a.closest('.o_editable').length) {
|
||||
|
||||
@@ -10,13 +10,13 @@
|
||||
<a href="https://twitter.com/intent/tweet?text={title}&url={url}" t-attf-class="s_share_twitter #{_link_classes}" target="_blank">
|
||||
<i t-attf-class="fa fa-twitter #{not _link_classes and 'rounded shadow-sm'}"/>
|
||||
</a>
|
||||
<a href="http://www.linkedin.com/sharing/share-offsite/?url={url}" t-attf-class="s_share_linkedin #{_link_classes}" target="_blank">
|
||||
<a href="https://www.linkedin.com/sharing/share-offsite/?url={url}" t-attf-class="s_share_linkedin #{_link_classes}" target="_blank">
|
||||
<i t-attf-class="fa fa-linkedin #{not _link_classes and 'rounded shadow-sm'}"/>
|
||||
</a>
|
||||
<a href="https://wa.me/?text={title}" t-attf-class="s_share_whatsapp #{_link_classes}" target="_blank">
|
||||
<i t-attf-class="fa fa-whatsapp #{not _link_classes and 'rounded shadow-sm'}"/>
|
||||
</a>
|
||||
<a href="http://pinterest.com/pin/create/button/?url={url}&description={title}" t-attf-class="s_share_pinterest #{_link_classes}" target="_blank">
|
||||
<a href="https://pinterest.com/pin/create/button/?url={url}&media={media}&description={title}" t-attf-class="s_share_pinterest #{_link_classes}" target="_blank">
|
||||
<i t-attf-class="fa fa-pinterest #{not _link_classes and 'rounded shadow-sm'}"/>
|
||||
</a>
|
||||
<a href="mailto:?body={url}&subject={title}" t-attf-class="s_share_email #{_link_classes}">
|
||||
|
||||
Reference in new issue
Block a user