diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..ebe3e0150 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,4 @@ +# convert to LF when writing to the object DB +* text=auto +*.py text +*.xml text diff --git a/.travis.yml b/.travis.yml index c655940e6..b07ac1df9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,65 +1,65 @@ -language: python -sudo: required -cache: - apt: true - directories: - - $HOME/.cache/pip - -python: - - "2.7" - -addons: - apt: - packages: - - expect-dev # provides unbuffer utility - - python-lxml # because pip installation is slow - - python-simplejson - - python-serial - - python-yaml - - python-cups - - python-mysqldb - - python-tk - # OpenCV requirements packages - - zbar-tools - - python-zbar - -env: - global: - - VERSION="10.0" TESTS="0" LINT_CHECK="0" TRANSIFEX="0" - - PHANTOMJS_VERSION="latest" - # The above line controls the PhantomJS version that is used for JS testing. - # It is not necessary to include this value unless you are altering the default. - # Use `OS` to skip the PhantomJS upgrade & use the system version instead. - - matrix: - - LINT_CHECK="1" - - TESTS="1" ODOO_REPO="odoo/odoo" EXCLUDE="pgsql_auto_backup" - - -virtualenv: - system_site_packages: true - -services: - - postgresql - -install: - - pip install -r requirements.txt - - git clone --depth=1 https://github.com/OCA/maintainer-quality-tools.git ${HOME}/maintainer-quality-tools - - export PATH=${HOME}/maintainer-quality-tools/travis:${PATH} - - travis_install_nightly - -before_script: - - sudo apt-get update - - sudo apt-get autoremove postgis* - - sudo apt-get autoremove postgresql* - - sudo apt-get install postgresql-9.3-postgis-2.1 - - sudo apt-get install python-opencv - - sudo dpkg -L python-opencv - - psql -U postgres -c "create extension postgis" - - psql -U postgres -c "create extension postgis_topology" - -script: - - travis_run_tests - -after_success: - - travis_after_tests_success +language: python +sudo: required +cache: + apt: true + directories: + - $HOME/.cache/pip + +python: + - "2.7" + +addons: + apt: + packages: + - expect-dev # provides unbuffer utility + - python-lxml # because pip installation is slow + - python-simplejson + - python-serial + - python-yaml + - python-cups + - python-mysqldb + - python-tk + # OpenCV requirements packages + - zbar-tools + - python-zbar + +env: + global: + - VERSION="10.0" TESTS="0" LINT_CHECK="0" TRANSIFEX="0" + - PHANTOMJS_VERSION="latest" + # The above line controls the PhantomJS version that is used for JS testing. + # It is not necessary to include this value unless you are altering the default. + # Use `OS` to skip the PhantomJS upgrade & use the system version instead. + + matrix: + - LINT_CHECK="1" + - TESTS="1" ODOO_REPO="odoo/odoo" EXCLUDE="pgsql_auto_backup" + + +virtualenv: + system_site_packages: true + +services: + - postgresql + +install: + - pip install -r requirements.txt + - git clone --depth=1 https://github.com/OCA/maintainer-quality-tools.git ${HOME}/maintainer-quality-tools + - export PATH=${HOME}/maintainer-quality-tools/travis:${PATH} + - travis_install_nightly + +before_script: + - sudo apt-get update + - sudo apt-get autoremove postgis* + - sudo apt-get autoremove postgresql* + - sudo apt-get install postgresql-9.3-postgis-2.1 + - sudo apt-get install python-opencv + - sudo dpkg -L python-opencv + - psql -U postgres -c "create extension postgis" + - psql -U postgres -c "create extension postgis_topology" + +script: + - travis_run_tests + +after_success: + - travis_after_tests_success diff --git a/account_reconcile_compassion/tests/__init__.py b/account_reconcile_compassion/tests/__init__.py new file mode 100644 index 000000000..cea535d1b --- /dev/null +++ b/account_reconcile_compassion/tests/__init__.py @@ -0,0 +1,11 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# Copyright (C) 2018 Compassion CH (http://www.compassion.ch) +# @author: Nicolas Badoux +# +# The licence is in the file __manifest__.py +# +############################################################################## + +from . import test_account_reconcile \ No newline at end of file diff --git a/account_reconcile_compassion/tests/test_account_reconcile.py b/account_reconcile_compassion/tests/test_account_reconcile.py new file mode 100644 index 000000000..2ae0bba85 --- /dev/null +++ b/account_reconcile_compassion/tests/test_account_reconcile.py @@ -0,0 +1,129 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# Copyright (C) 2018 Compassion CH (http://www.compassion.ch) +# @author: Quentin Gigon +# +# The licence is in the file __manifest__.py +# +############################################################################## + +from odoo import fields, _ +from odoo.addons.sponsorship_compassion.tests.test_sponsorship_compassion \ + import BaseSponsorshipTest + + +class TestAccountReconcile(BaseSponsorshipTest): + + def setUp(self): + super(TestAccountReconcile, self).setUp() + + self.t_child = self.create_child('TT123456789') + self.t_partner = self.env['res.users'].search([ + ('company_id', '=', 1) + ], limit=1) + t_group = self.create_group({'partner_id': self.t_partner.id}) + self.t_sponsorship = self.create_contract({ + 'partner_id': self.t_partner.id, + 'group_id': t_group.id, + 'child_id': self.t_child.id, + }, + [{'amount': 50.0}]) + + self.company = self.env['res.company'].search([ + ('id', '=', 1) + ], limit=1) + + self.journal = self.env['account.journal'].search([ + ('code', '=', 'CCP') + ]) + + self.account = self.env['account.account'].search([ + ('id', '=', self.journal.default_credit_account_id.id) + ]) + self.account.write({'currency_id': self.env.ref('base.CHF').id}) + + def test_account_reconcile(self): + self.assertTrue(self.journal) + self.assertTrue(self.company) + self.assertTrue(self.account) + + move = self.env['account.move'].create({ + 'name': 'test_acc_move', + 'date': fields.Date.today(), + 'journal_id': self.journal.id, + 'state': 'draft', + 'company_id': self.company.id, + 'ref': 'test_ref' + }) + self.assertTrue(move) + + account_move_line = self.env['account.move.line'].create({ + 'name': 'test_move_line', + 'account_id': self.account.id, + 'move_id': move.id, + 'date_maturity': '2018-12-12', + 'currency_id': self.env.ref('base.CHF').id + }) + self.assertTrue(account_move_line) + + account_move_line_today = self.env['account.move.line'].create({ + 'name': 'test_move_line', + 'account_id': self.account.id, + 'move_id': move.id, + 'date_maturity': fields.Date.today(), + 'currency_id': self.env.ref('base.CHF').id + }) + self.assertTrue(account_move_line_today) + + bank_statement = self.env['account.bank.statement'].create({ + 'date': fields.Date.today(), + 'state': 'open', + 'journal_id': self.journal.id, + 'move_line_ids': [(6, _, [account_move_line.id, + account_move_line_today.id])] + }) + self.assertTrue(bank_statement) + + bank_statement_line = self.env['account.bank.statement.line'].create({ + 'name': 'TestBankLine', + 'date': fields.Date.today(), + 'amount': 50, + 'journal_id': self.journal.id, + 'account_id': self.account.id, + 'statement_id': bank_statement.id, + 'ref': 'test_ref', + 'currency_id': self.account.currency_id.id, + 'journal_entry_ids': [(6, _, [move.id])] + }) + self.assertTrue(bank_statement_line) + + # should be 12 - 6 * 12 = 72 + self.assertEquals(bank_statement_line._sort_move_line( + account_move_line), 72) + # should be 1 + self.assertEquals(bank_statement_line._sort_move_line( + account_move_line_today), 1) + + # test get_move_lines_for_reconciliation method + self.assertEquals( + len(bank_statement_line.get_move_lines_for_reconciliation()), 0) + + # test linking partner to bank when writing to + # account.bank.statement.line + self.env['account.bank.statement.line'].write({ + 'partner_id': self.t_partner.id + }) + partner_bank = self.env['res.partner.bank'].search([ + '|', + ('acc_number', 'like', self.journal.bank_account_id.acc_number), + ('sanitized_acc_number', 'like', + self.journal.bank_account_id.acc_number) + ]) + self.assertEquals(partner_bank.company_id, self.journal.company_id) + + acc_partial_rec = self.env['account.partial.reconcile'].create({ + 'debit_move_id': account_move_line.id, + 'credit_move_id': account_move_line.id + }) + self.assertTrue(acc_partial_rec) diff --git a/cleanup_switzerland/data/cleanup_cron.xml b/cleanup_switzerland/data/cleanup_cron.xml index 440f7aaf6..5ea4c30f0 100644 --- a/cleanup_switzerland/data/cleanup_cron.xml +++ b/cleanup_switzerland/data/cleanup_cron.xml @@ -1,13 +1,15 @@ - - CH Database Cleanup - 1 - months - -1 - - cleanup - database.cleanup.switzerland - + + + CH Database Cleanup + 1 + months + -1 + + cleanup + database.cleanup.switzerland + + diff --git a/lsv_compassion/README.rst b/lsv_compassion/README.rst index 579bb6877..947852cc0 100644 --- a/lsv_compassion/README.rst +++ b/lsv_compassion/README.rst @@ -1,38 +1,38 @@ -.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg - :alt: License: AGPL-3 - -LSV-DD Export Compassion -======================== - -Customize LSV-DD to fit Compassion needs. - -* Change communication in Direct Debit Orders - -Installation -============ -This modules requires en_US, fr_CH, de_DE, it_IT and es_ES to be installed -on the server. - -To check installed locales: - -* locale -a - -To add a new locale : - -* /usr/share/locales/install-language-pack -* dpkg-reconfigure locales - -Credits -======= - -Contributors ------------- - -* Cyril Sester -* Emanuel Cino -* Nicolas Tran - -Maintainer ----------- - -This module is maintained by `Compassion Switzerland `. +.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg + :alt: License: AGPL-3 + +LSV-DD Export Compassion +======================== + +Customize LSV-DD to fit Compassion needs. + +* Change communication in Direct Debit Orders + +Installation +============ +This modules requires en_US, fr_CH, de_DE, it_IT and es_ES to be installed +on the server. + +To check installed locales: + +* locale -a + +To add a new locale : + +* /usr/share/locales/install-language-pack +* dpkg-reconfigure locales + +Credits +======= + +Contributors +------------ + +* Cyril Sester +* Emanuel Cino +* Nicolas Tran + +Maintainer +---------- + +This module is maintained by `Compassion Switzerland `. diff --git a/lsv_compassion/__init__.py b/lsv_compassion/__init__.py index aa3393fe1..4fa919581 100644 --- a/lsv_compassion/__init__.py +++ b/lsv_compassion/__init__.py @@ -1,11 +1,11 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# Copyright (C) 2014 Compassion CH (http://www.compassion.ch) -# Releasing children from poverty in Jesus' name -# @author: Cyril Sester -# -# The licence is in the file __manifest__.py -# -############################################################################## -from . import models +# -*- coding: utf-8 -*- +############################################################################## +# +# Copyright (C) 2014 Compassion CH (http://www.compassion.ch) +# Releasing children from poverty in Jesus' name +# @author: Cyril Sester +# +# The licence is in the file __manifest__.py +# +############################################################################## +from . import models diff --git a/multi_attachment_switzerland/__init__.py b/multi_attachment_switzerland/__init__.py index 86b8be75f..e0d08c4bd 100644 --- a/multi_attachment_switzerland/__init__.py +++ b/multi_attachment_switzerland/__init__.py @@ -1,10 +1,10 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# Copyright (C) 2014 Compassion CH (http://www.compassion.ch) -# Releasing children from poverty in Jesus' name -# @author: Michael Sandoz -# -# The licence is in the file __manifest__.py -# -############################################################################## +# -*- coding: utf-8 -*- +############################################################################## +# +# Copyright (C) 2014 Compassion CH (http://www.compassion.ch) +# Releasing children from poverty in Jesus' name +# @author: Michael Sandoz +# +# The licence is in the file __manifest__.py +# +############################################################################## diff --git a/oca_dependencies.txt b/oca_dependencies.txt index e2abee3d6..a7a1d9b6e 100644 --- a/oca_dependencies.txt +++ b/oca_dependencies.txt @@ -26,5 +26,5 @@ survey bank-payment web https://github.com/daramousk/web 10.0-MIG-CKEditor l10n-switzerland https://github.com/CompassionCH/l10n-switzerland 10.0-pain-sepa -compassion-accounting https://github.com/CompassionCH/compassion-accounting 10.0 -compassion-modules https://github.com/CompassionCH/compassion-modules 10.0 +compassion-accounting https://github.com/CompassionCH/compassion-accounting devel +compassion-modules https://github.com/CompassionCH/compassion-modules devel \ No newline at end of file diff --git a/partner_communication_switzerland/__init__.py b/partner_communication_switzerland/__init__.py index fd41bef47..d881c8c14 100644 --- a/partner_communication_switzerland/__init__.py +++ b/partner_communication_switzerland/__init__.py @@ -11,3 +11,4 @@ from . import models from . import wizards +from . import forms diff --git a/partner_communication_switzerland/__manifest__.py b/partner_communication_switzerland/__manifest__.py index e844b16de..c92d8e207 100644 --- a/partner_communication_switzerland/__manifest__.py +++ b/partner_communication_switzerland/__manifest__.py @@ -30,7 +30,7 @@ # pylint: disable=C8101 { 'name': 'Compassion CH Partner Communications', - 'version': '10.0.2.4.4', + 'version': '10.0.2.4.5', 'category': 'Other', 'author': 'Compassion CH', 'license': 'AGPL-3', @@ -38,7 +38,8 @@ 'depends': [ 'report_compassion', 'child_switzerland', - 'partner_compassion' + 'partner_compassion', + 'sms_sponsorship' ], 'external_dependencies': { 'python': ['wand', 'detectlanguage', 'sendgrid'] diff --git a/partner_communication_switzerland/data/communication_config.xml b/partner_communication_switzerland/data/communication_config.xml index 665352dc8..3eced62d0 100644 --- a/partner_communication_switzerland/data/communication_config.xml +++ b/partner_communication_switzerland/data/communication_config.xml @@ -226,6 +226,15 @@ get_birthday_bvr + + Sponsorship - 1 Day Birthday Reminder + + + auto_digital + + + get_birthday_bvr + Sponsorship - Anniversary 1 year @@ -504,6 +513,14 @@ + + SMS sponsorship confirmation step 2 + auto_digital + + + + + diff --git a/partner_communication_switzerland/data/sponsorship_planned_emails.xml b/partner_communication_switzerland/data/sponsorship_planned_emails.xml index 0692fd42b..dd3103ae7 100644 --- a/partner_communication_switzerland/data/sponsorship_planned_emails.xml +++ b/partner_communication_switzerland/data/sponsorship_planned_emails.xml @@ -374,6 +374,20 @@ + + + Sponsorship - 1 Day Birthday Reminder + + compassion@compassion.ch + info@compassion.ch + ${object.partner_id and object.partner_id.email and object.partner_id.id or False } + Wish ${object.get_objects().get('your sponsored child')} a Happy Birthday ! + +

To define

+
+
+ + diff --git a/partner_communication_switzerland/forms/__init__.py b/partner_communication_switzerland/forms/__init__.py new file mode 100644 index 000000000..9b296c96a --- /dev/null +++ b/partner_communication_switzerland/forms/__init__.py @@ -0,0 +1,12 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# Copyright (C) 2016 Compassion CH (http://www.compassion.ch) +# Releasing children from poverty in Jesus' name +# @author: Emanuel Cino +# +# The licence is in the file __manifest__.py +# +############################################################################## + +from . import sms_sponsorship_registration_form diff --git a/partner_communication_switzerland/forms/sms_sponsorship_registration_form.py b/partner_communication_switzerland/forms/sms_sponsorship_registration_form.py new file mode 100644 index 000000000..c5260c697 --- /dev/null +++ b/partner_communication_switzerland/forms/sms_sponsorship_registration_form.py @@ -0,0 +1,29 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# Copyright (C) 2018 Compassion CH (http://www.compassion.ch) +# @author: Quentin Gigon +# +# The licence is in the file __manifest__.py +# +############################################################################## +from odoo import models, fields, tools + +testing = tools.config.get('test_enable') + + +if not testing: + class PartnerSmsRegistrationForm(models.AbstractModel): + _inherit = 'cms.form.recurring.contract' + + payment_mode_id = fields.Many2one( + 'account.payment.mode', + domain=[('name', 'in', ['LSV', 'Postfinance Direct Debit', + 'Permanent Order'])]) + + def _send_confirmation_mail(self): + # send confirmation mail + config = self.env.ref( + 'partner_communication_switzerland.' + 'sms_registration_confirmation') + self.main_object.send_communication(config) diff --git a/partner_communication_switzerland/migrations/10.0.2.4.5/pre-migration.py b/partner_communication_switzerland/migrations/10.0.2.4.5/pre-migration.py new file mode 100644 index 000000000..fee8ac643 --- /dev/null +++ b/partner_communication_switzerland/migrations/10.0.2.4.5/pre-migration.py @@ -0,0 +1,50 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# Copyright (C) 2018 Compassion CH (http://www.compassion.ch) +# Releasing children from poverty in Jesus' name +# @author: Nicolas Bornand +# +# The licence is in the file __manifest__.py +# +############################################################################## +from openupgradelib import openupgrade + + +@openupgrade.migrate(use_env=True) +def migrate(env, version): + if not version: + return + + env.cr.execute(""" + SELECT c.id + FROM partner_communication_config c + JOIN utm_source s ON c.source_id = s.id + WHERE s.name ILIKE '%1 Day Birthday Reminder%' + """) + one_day_reminder_config = env.cr.fetchone() + if one_day_reminder_config: + openupgrade.add_xmlid( + env.cr, + module='partner_communication_switzerland', + xmlid='birthday_remainder_1day_before', + model='partner.communication.config', + res_id=one_day_reminder_config[0], + noupdate=True + ) + + env.cr.execute(""" + SELECT id + FROM mail_template + WHERE name ILIKE '%1 Day Birthday Reminder%' + """) + one_day_reminder_template = env.cr.fetchone() + if one_day_reminder_template: + openupgrade.add_xmlid( + env.cr, + module='partner_communication_switzerland', + xmlid='email_sponsorship_birthday_1day_reminder', + model='mail.template', + res_id=one_day_reminder_template[0], + noupdate=True + ) diff --git a/partner_communication_switzerland/models/contracts.py b/partner_communication_switzerland/models/contracts.py index 1af993173..6824cf57d 100644 --- a/partner_communication_switzerland/models/contracts.py +++ b/partner_communication_switzerland/models/contracts.py @@ -91,9 +91,10 @@ def _compute_due_invoices(self): if contract.child_id.project_id.suspension != 'fund-suspended': invoice_lines = contract.invoice_line_ids.with_context( lang='en_US').filtered( - lambda i: i.state == 'open' and - fields.Date.from_string(i.due_date) < this_month and - i.invoice_id.invoice_type == 'sponsorship' + lambda i: i.state == 'open' and + fields.Date.from_string( + i.due_date) < this_month and + i.invoice_id.invoice_type == 'sponsorship' ) contract.due_invoice_ids = invoice_lines.mapped('invoice_id') contract.amount_due = int(sum(invoice_lines.mapped( @@ -153,12 +154,12 @@ def send_communication(self, communication, correspondent=True, if contract.correspondent_id != contract.partner_id: communications += self.env[ 'partner.communication.job'].create({ - 'config_id': communication.id, - 'partner_id': contract.correspondent_id.id, - 'object_ids': self.env.context.get( - 'default_object_ids', contract.id), - 'user_id': communication.user_id.id, - }) + 'config_id': communication.id, + 'partner_id': contract.correspondent_id.id, + 'object_ids': self.env.context.get( + 'default_object_ids', contract.id), + 'user_id': communication.user_id.id, + }) else: for partner in partners: objects = self.filtered( @@ -206,18 +207,61 @@ def send_monthly_communication(self): def send_daily_communication(self): """ Prepare daily communications to send. - - Welcome letters for started sponsorships since 10 days (only e-mail) + - Welcome letters for started sponsorships since 1 day (only e-mail) - Birthday reminders """ - module = 'partner_communication_switzerland.' logger.info("Sponsorship Planned Communications started!") - # Birthday Reminder + logger.info("....Creating Birthday Reminder Communications") + self._send_reminders_for_birthday_in_1day_or_2months() + + logger.info("....Send Welcome Activations Letters") + self._send_welcome_letters_for_sponsorships_activated_in_last_24h() + + logger.info("Sponsorship Planned Communications finished!") + + @api.model + def _send_reminders_for_birthday_in_1day_or_2months(self): + module = 'partner_communication_switzerland.' logger.info("....Creating Birthday Reminder Communications") today = datetime.now() + in_two_month = today + relativedelta(months=2) - birthday = self.search([ - ('child_id.birthdate', 'like', in_two_month.strftime("%%-%m-%d")), + sponsorships_with_birthday_in_two_months = \ + self._get_sponsorships_with_child_birthday_on(in_two_month) + self._send_birthday_reminders( + sponsorships_with_birthday_in_two_months, + self.env.ref(module + 'planned_birthday_reminder') + ) + + tomorrow = today + relativedelta(days=1) + sponsorships_with_birthday_tomorrow = \ + self._get_sponsorships_with_child_birthday_on(tomorrow) + self._send_birthday_reminders( + sponsorships_with_birthday_tomorrow, + self.env.ref(module + 'birthday_remainder_1day_before') + ) + + @api.model + def _send_birthday_reminders(self, sponsorships, communication): + communication_jobs = self.env['partner.communication.job'] + for sponsorship in sponsorships: + send_to_partner_as_he_paid_the_gift = \ + sponsorship.send_gifts_to == 'partner_id' + try: + communication_jobs += sponsorship.send_communication( + communication, + correspondent=True, + both=send_to_partner_as_he_paid_the_gift + ) + except Exception: + # In any case, we don't want to stop email generation! + logger.error("Error during birthday reminder: ", exc_info=True) + + @api.model + def _get_sponsorships_with_child_birthday_on(self, birth_day): + return self.search([ + ('child_id.birthdate', 'like', birth_day.strftime("%%-%m-%d")), '|', ('correspondent_id.birthday_reminder', '=', True), ('partner_id.birthday_reminder', '=', True), '|', ('correspondent_id.email', '!=', False), @@ -228,39 +272,22 @@ def send_daily_communication(self): ]).filtered(lambda c: not ( c.child_id.project_id.lifecycle_ids and c.child_id.project_id.hold_s2b_letters) - ) - config = self.env.ref(module + 'planned_birthday_reminder') - comms = self.env['partner.communication.job'] - for sponsorship in birthday: - - # Send the communication to the correspondent in any case. - correspondent = True - - # Send the communication to both the partner and correspondent - # if the partner is the one who paid the gift. - send_to_both = sponsorship.send_gifts_to == 'partner_id' + ) - try: - comms += sponsorship.send_communication(config, - correspondent, - send_to_both) - except: - # In any case, we don't want to stop email generation! - continue - - # send welcome activations letters + @api.model + def _send_welcome_letters_for_sponsorships_activated_in_last_24h(self): welcome = self.env.ref( 'partner_communication_switzerland.welcome_activation') + activated_since = fields.Datetime.to_string( + datetime.today() - timedelta(days=1)) to_send = self.env['recurring.contract'].search([ - ('activation_date', '>=', (datetime.today() - timedelta(days=1))), + ('activation_date', '>=', activated_since), ('child_id', '!=', False) ]) if to_send: to_send.send_communication(welcome, both=True).send() to_send.write({'sds_state': 'active'}) - logger.info("Sponsorship Planned Communications finished!") - @api.model def send_sponsorship_reminders(self): logger.info("Creating Sponsorship Reminders") @@ -281,17 +308,17 @@ def send_sponsorship_reminders(self): twenty_ago = today - relativedelta(days=20) comm_obj = self.env['partner.communication.job'] for sponsorship in self.search([ - ('state', 'in', ('active', 'mandate')), - ('global_id', '!=', False), - ('type', 'like', 'S'), - '|', - ('child_id.project_id.suspension', '!=', 'fund-suspended'), - ('child_id.project_id.suspension', '=', False), + ('state', 'in', ('active', 'mandate')), + ('global_id', '!=', False), + ('type', 'like', 'S'), + '|', + ('child_id.project_id.suspension', '!=', 'fund-suspended'), + ('child_id.project_id.suspension', '=', False), ]): due = sponsorship.due_invoice_ids advance_billing = sponsorship.group_id.advance_billing_months if due and len(due) > 1 and not (advance_billing > 1 and len( - due) < 3): + due) < 3): has_first_reminder = comm_obj.search_count([ ('config_id', 'in', [first_reminder_config.id, second_reminder_config.id]), @@ -370,10 +397,10 @@ def contract_waiting_mandate(self): new_spons._new_dossier() new_spons.filtered( lambda s: s.correspondent_id.email and s.sds_state == 'draft' and - s.partner_id.ref != '1502623').write({ - 'sds_state': 'waiting_welcome', - 'sds_state_date': fields.Date.today() - }) + s.partner_id.ref != '1502623').write({ + 'sds_state': 'waiting_welcome', + 'sds_state_date': fields.Date.today() + }) if 'CSP' in self.name: module = 'partner_communication_switzerland.' selected_config = self.env.ref(module + 'csp_mail') @@ -386,7 +413,8 @@ def contract_waiting(self): # Waiting welcome for partners with e-mail (except Demaurex) welcome = self.filtered( lambda s: 'S' in s.type and s.sds_state == 'draft' and - s.correspondent_id.email and s.partner_id.ref != '1502623') + s.correspondent_id.email and s.partner_id.ref != + '1502623') welcome.write({ 'sds_state': 'waiting_welcome' }) @@ -395,7 +423,7 @@ def contract_waiting(self): res = super(RecurringContract, self).contract_waiting() self.filtered( lambda c: 'S' in c.type and not c.is_active and c not in - mandates_valid + mandates_valid )._new_dossier() if 'CSP' in self.name: diff --git a/partner_compassion/README.rst b/partner_compassion/README.rst index 38d8636a0..7a905b4af 100644 --- a/partner_compassion/README.rst +++ b/partner_compassion/README.rst @@ -1,51 +1,51 @@ -.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg - :alt: License: AGPL-3 - -Upgrade Partners for Compassion Suisse -====================================== - -A. Upgrade Partners to Compassion Switzerland standards : - - Add correspondance information - - Redefines the views of the partners - - Add partner geolocalization - -B. E-mail tracking : - - Post e-mails sent with Sendgrid in the partners - - Track the reception of message inside the thread - - Restrict e-mail followers - -C. Add ambassador details information - -Configuration -============= -Add the following parameters to your Odoo configuration file: - -* ``smb_user`` : user for connecting on the NAS of Compassion with Samba -* ``smb_pwd`` : password for Samba -* ``smb_ip`` : IP address of the NAS of Compassion -* ``smb_port`` : Samba port of the NAS -* ``partner_data_password`` : The password for encrypted ZIP file containing erased partner history - -Add the following system parameters in Odoo->Settings->System Parameters - -* ``partner_compassion.share_on_nas`` : Name of the Samba root share -* ``partner_compassion.store_path`` : Path to the ZIP file containing erased partner history - -Known issues / Roadmap -====================== - -* Missing tests for mail_message and mail_thread - -Credits -======= - -Contributors ------------- - -* Emanuel Cino -* Steve Ferry - -Maintainer ----------- - -This module is maintained by `Compassion Switzerland `. +.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg + :alt: License: AGPL-3 + +Upgrade Partners for Compassion Suisse +====================================== + +A. Upgrade Partners to Compassion Switzerland standards : + - Add correspondance information + - Redefines the views of the partners + - Add partner geolocalization + +B. E-mail tracking : + - Post e-mails sent with Sendgrid in the partners + - Track the reception of message inside the thread + - Restrict e-mail followers + +C. Add ambassador details information + +Configuration +============= +Add the following parameters to your Odoo configuration file: + +* ``smb_user`` : user for connecting on the NAS of Compassion with Samba +* ``smb_pwd`` : password for Samba +* ``smb_ip`` : IP address of the NAS of Compassion +* ``smb_port`` : Samba port of the NAS +* ``partner_data_password`` : The password for encrypted ZIP file containing erased partner history + +Add the following system parameters in Odoo->Settings->System Parameters + +* ``partner_compassion.share_on_nas`` : Name of the Samba root share +* ``partner_compassion.store_path`` : Path to the ZIP file containing erased partner history + +Known issues / Roadmap +====================== + +* Missing tests for mail_message and mail_thread + +Credits +======= + +Contributors +------------ + +* Emanuel Cino +* Steve Ferry + +Maintainer +---------- + +This module is maintained by `Compassion Switzerland `. diff --git a/partner_compassion/data/partner_category_data.xml b/partner_compassion/data/partner_category_data.xml index c45fbd145..9c35a1eb5 100644 --- a/partner_compassion/data/partner_category_data.xml +++ b/partner_compassion/data/partner_category_data.xml @@ -17,10 +17,6 @@ Ambassador - - Church - - Translator diff --git a/partner_compassion/migrations/8.0.2.1/post-migration.py b/partner_compassion/migrations/8.0.2.1/post-migration.py index c4cd1a8ce..a0df52456 100644 --- a/partner_compassion/migrations/8.0.2.1/post-migration.py +++ b/partner_compassion/migrations/8.0.2.1/post-migration.py @@ -1,20 +1,20 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# Copyright (C) 2015 Compassion CH (http://www.compassion.ch) -# Releasing children from poverty in Jesus' name -# @author: Emanuel Cino -# -# The licence is in the file __manifest__.py -# -############################################################################## - - -def migrate(cr, version): - if not version: - return - - cr.execute(""" - UPDATE res_partner SET birthdate = birthdate_backup; - ALTER TABLE res_partner DROP COLUMN birthdate_backup; - """) +# -*- coding: utf-8 -*- +############################################################################## +# +# Copyright (C) 2015 Compassion CH (http://www.compassion.ch) +# Releasing children from poverty in Jesus' name +# @author: Emanuel Cino +# +# The licence is in the file __manifest__.py +# +############################################################################## + + +def migrate(cr, version): + if not version: + return + + cr.execute(""" + UPDATE res_partner SET birthdate = birthdate_backup; + ALTER TABLE res_partner DROP COLUMN birthdate_backup; + """) diff --git a/partner_compassion/migrations/8.0.2.1/pre-migration.py b/partner_compassion/migrations/8.0.2.1/pre-migration.py index 5efeb07e8..917de3849 100644 --- a/partner_compassion/migrations/8.0.2.1/pre-migration.py +++ b/partner_compassion/migrations/8.0.2.1/pre-migration.py @@ -1,20 +1,20 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# Copyright (C) 2016 Compassion CH (http://www.compassion.ch) -# Releasing children from poverty in Jesus' name -# @author: Emanuel Cino -# -# The licence is in the file __manifest__.py -# -############################################################################## - - -def migrate(cr, version): - if not version: - return - - cr.execute(""" - ALTER TABLE res_partner ADD COLUMN birthdate_backup VARCHAR; - UPDATE res_partner SET birthdate_backup = birthdate::VARCHAR; - """) +# -*- coding: utf-8 -*- +############################################################################## +# +# Copyright (C) 2016 Compassion CH (http://www.compassion.ch) +# Releasing children from poverty in Jesus' name +# @author: Emanuel Cino +# +# The licence is in the file __manifest__.py +# +############################################################################## + + +def migrate(cr, version): + if not version: + return + + cr.execute(""" + ALTER TABLE res_partner ADD COLUMN birthdate_backup VARCHAR; + UPDATE res_partner SET birthdate_backup = birthdate::VARCHAR; + """) diff --git a/partner_compassion/migrations/8.0.2/pre-migration.py b/partner_compassion/migrations/8.0.2/pre-migration.py index 734b7df47..881b78e67 100644 --- a/partner_compassion/migrations/8.0.2/pre-migration.py +++ b/partner_compassion/migrations/8.0.2/pre-migration.py @@ -1,36 +1,36 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# Copyright (C) 2015 Compassion CH (http://www.compassion.ch) -# Releasing children from poverty in Jesus' name -# @author: Emanuel Cino -# -# The licence is in the file __manifest__.py -# -############################################################################## - -import logging -logger = logging.getLogger() - - -def remove_old_views(cr): - """ Remove old views to avoid errors. """ - cr.execute( - "SELECT res_id FROM ir_model_data " - "WHERE module = 'partner_compassion' " - "AND model = 'ir.ui.view' ") - view_ids = [str(row[0]) for row in cr.fetchall()] - cr.execute( - "DELETE FROM ir_ui_view " - "WHERE inherit_id IN (%s) " % ','.join(view_ids)) - cr.execute( - "DELETE FROM ir_ui_view " - "WHERE id IN (%s) " % ','.join(view_ids)) - - -def migrate(cr, version): - if not version: - return - - # Change type of analytic accounts - remove_old_views(cr) +# -*- coding: utf-8 -*- +############################################################################## +# +# Copyright (C) 2015 Compassion CH (http://www.compassion.ch) +# Releasing children from poverty in Jesus' name +# @author: Emanuel Cino +# +# The licence is in the file __manifest__.py +# +############################################################################## + +import logging +logger = logging.getLogger() + + +def remove_old_views(cr): + """ Remove old views to avoid errors. """ + cr.execute( + "SELECT res_id FROM ir_model_data " + "WHERE module = 'partner_compassion' " + "AND model = 'ir.ui.view' ") + view_ids = [str(row[0]) for row in cr.fetchall()] + cr.execute( + "DELETE FROM ir_ui_view " + "WHERE inherit_id IN (%s) " % ','.join(view_ids)) + cr.execute( + "DELETE FROM ir_ui_view " + "WHERE id IN (%s) " % ','.join(view_ids)) + + +def migrate(cr, version): + if not version: + return + + # Change type of analytic accounts + remove_old_views(cr) diff --git a/partner_compassion/models/partner_compassion.py b/partner_compassion/models/partner_compassion.py index 64d2af329..0a0f09460 100644 --- a/partner_compassion/models/partner_compassion.py +++ b/partner_compassion/models/partner_compassion.py @@ -51,13 +51,6 @@ def _get_receipt_types(self): total_invoiced = fields.Monetary(groups=False) street3 = fields.Char("Street3", size=128) invalid_mail = fields.Char("Invalid mail") - member_ids = fields.One2many( - 'res.partner', 'church_id', 'Members', - domain=[('active', '=', True)]) - is_church = fields.Boolean( - string="Is a Church", compute='_compute_is_church', store=True) - church_id = fields.Many2one( - 'res.partner', 'Church', domain=[('is_church', '=', True)]) church_unlinked = fields.Char( "Church (N/A)", help="Use this field if the church of the partner" @@ -93,8 +86,6 @@ def _get_receipt_types(self): partner_duplicate_ids = fields.Many2many( 'res.partner', 'res_partner_duplicates', 'partner_id', 'duplicate_id', readonly=True) - church_member_count = fields.Integer(compute='_compute_is_church', - store=True) ambassador_details_id = fields.Many2one('ambassador.details', 'Details of ambassador') @@ -108,23 +99,6 @@ def _get_receipt_types(self): ########################################################################## # FIELDS METHODS # ########################################################################## - @api.multi - @api.depends('category_id', 'member_ids') - def _compute_is_church(self): - """ Tell if the given Partners are Church Partners - (by looking at their categories). """ - - # Retrieve all the categories and check if one is Church - church_category = self.env['res.partner.category'].with_context( - lang='en_US').search([('name', '=', 'Church')], limit=1) - for record in self: - is_church = False - if church_category in record.category_id: - is_church = True - - record.church_member_count = len(record.member_ids) - record.is_church = is_church - @api.multi def get_unreconciled_amount(self): """Returns the amount of unreconciled credits in Account 1050""" diff --git a/partner_compassion/views/partner_compassion_view.xml b/partner_compassion/views/partner_compassion_view.xml index c6a036359..f86faa6fb 100644 --- a/partner_compassion/views/partner_compassion_view.xml +++ b/partner_compassion/views/partner_compassion_view.xml @@ -36,6 +36,10 @@ + + + + @@ -79,14 +83,9 @@ - + - - - @@ -95,16 +94,6 @@ - - - - - - - - - - diff --git a/sms_939/README.rst b/sms_939/README.rst new file mode 100755 index 000000000..f31ec67a5 --- /dev/null +++ b/sms_939/README.rst @@ -0,0 +1,94 @@ +================ +939 SMS Services +================ + +.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png + :target: https://odoo-community.org/page/development-status + :alt: Beta +.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-CompassionCH%2Fcompassion--switzerland-lightgray.png?logo=github + :target: https://github.com/CompassionCH/compassion-switzerland/tree/devel/sms_939 + :alt: CompassionCH/compassion-switzerland + +|badge1| |badge2| |badge3| + +This module integrates the services of 939_ into Odoo. It can send SMS messages and receive SMS notifications. + +.. _939: http://www.939.ch/ + +**Table of contents** + +.. contents:: + :local: + +Configuration +============= + +Make sure you have setup your 939 account in your odoo.conf file + +#. 939_username = XXX +#. 939_password = YYY + +Usage +===== + +To use this module, you need to: + +#. Have a mobile app that can send messages to your Odoo instance + +Known issues / Roadmap +====================== + +* Nothing yet + +Changelog +========= + +10.0.1.0.0 (2018-07-09) +~~~~~~~~~~~~~~~~~~~~~~~ + +* [ADD] Add the module + +Bug Tracker +=========== + +Bugs are tracked on `GitHub 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 `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +~~~~~~~ + +* Compassion CH + +Contributors +~~~~~~~~~~~~ + +* Emanuel Cino + +Maintainers +~~~~~~~~~~~ + +This module is maintained by Compassion Switzerland. + +.. image:: https://upload.wikimedia.org/wikipedia/en/8/83/CompassionInternationalLogo.png + :alt: Compassion Switzerland + :target: https://www.compassion.ch + +Compassion Switzerland is a nonprofit organization whose +mission is to release children from extreme poverty in Jesus name. + +This module is part of the `CompassionCH/compassion-switzerland `_ project on GitHub. diff --git a/sms_939/__init__.py b/sms_939/__init__.py new file mode 100644 index 000000000..edefb0efb --- /dev/null +++ b/sms_939/__init__.py @@ -0,0 +1,14 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# Copyright (C) 2018 Compassion CH (http://www.compassion.ch) +# Releasing children from poverty in Jesus' name +# @author: Emanuel Cino +# +# The licence is in the file __manifest__.py +# +############################################################################## + +from . import models +from . import tools +from . import controllers diff --git a/sms_939/__manifest__.py b/sms_939/__manifest__.py new file mode 100644 index 000000000..9a26274e6 --- /dev/null +++ b/sms_939/__manifest__.py @@ -0,0 +1,51 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# ______ Releasing children from poverty _ +# / ____/___ ____ ___ ____ ____ ___________(_)___ ____ +# / / / __ \/ __ `__ \/ __ \/ __ `/ ___/ ___/ / __ \/ __ \ +# / /___/ /_/ / / / / / / /_/ / /_/ (__ |__ ) / /_/ / / / / +# \____/\____/_/ /_/ /_/ .___/\__,_/____/____/_/\____/_/ /_/ +# /_/ +# in Jesus' name +# +# Copyright (C) 2018 Compassion CH (http://www.compassion.ch) +# @author: Emanuel Cino +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +# pylint: disable=C8101 +{ + 'name': '939 SMS Services', + 'version': '10.0.1.0.0', + 'category': 'Other', + 'author': 'Compassion CH', + 'license': 'AGPL-3', + 'website': 'https://github.com/CompassionCH/compassion-switzerland/' + 'tree/10.0', + 'depends': ['sms_sponsorship'], + 'data': [ + 'security/ir.model.access.csv', + 'data/sms_hook.xml', + 'views/sms_views.xml' + ], + 'demo': [ + 'demo/sms_hook.xml' + ], + 'development_status': 'Beta', + 'installable': True, + 'auto_install': False, +} diff --git a/sms_939/controllers/__init__.py b/sms_939/controllers/__init__.py new file mode 100644 index 000000000..e4059066a --- /dev/null +++ b/sms_939/controllers/__init__.py @@ -0,0 +1,12 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# Copyright (C) 2018 Compassion CH (http://www.compassion.ch) +# Releasing children from poverty in Jesus' name +# @author: Emanuel Cino +# +# The licence is in the file __manifest__.py +# +############################################################################## + +from . import sms_notification_controller diff --git a/sms_939/controllers/sms_notification_controller.py b/sms_939/controllers/sms_notification_controller.py new file mode 100644 index 000000000..702c6fd09 --- /dev/null +++ b/sms_939/controllers/sms_notification_controller.py @@ -0,0 +1,31 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# Copyright (C) 2018 Compassion CH (http://www.compassion.ch) +# Releasing children from poverty in Jesus' name +# @author: Emanuel Cino +# +# The licence is in the file __manifest__.py +# +############################################################################## + +from odoo import http +from odoo.http import request + + +class RestController(http.Controller): + + @http.route('/sms/939/', type='http', auth='public', methods=['GET'], + csrf=False) + def sms_notification(self, **parameters): + sms = request.env['sms.notification'].sudo().create({ + 'instance': parameters.get('instance'), + 'sender': parameters.get('sender'), + 'operator': parameters.get('operator'), + 'service': parameters.get('service'), + 'language': parameters.get('language'), + 'date': parameters.get('receptionDate'), + 'uuid': parameters.get('requestUid'), + 'text': parameters.get('text'), + }) + return sms.run_service() diff --git a/sms_939/data/sms_hook.xml b/sms_939/data/sms_hook.xml new file mode 100644 index 000000000..0c184bd8e --- /dev/null +++ b/sms_939/data/sms_hook.xml @@ -0,0 +1,19 @@ + + + + + + + diff --git a/sms_939/demo/sms_hook.xml b/sms_939/demo/sms_hook.xml new file mode 100644 index 000000000..1e2c7c37d --- /dev/null +++ b/sms_939/demo/sms_hook.xml @@ -0,0 +1,7 @@ + + + + TEST + test_service + + diff --git a/sms_939/models/__init__.py b/sms_939/models/__init__.py new file mode 100644 index 000000000..6a4e7eba1 --- /dev/null +++ b/sms_939/models/__init__.py @@ -0,0 +1,13 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# Copyright (C) 2018 Compassion CH (http://www.compassion.ch) +# Releasing children from poverty in Jesus' name +# @author: Emanuel Cino +# +# The licence is in the file __manifest__.py +# +############################################################################## + +from . import sms_hook +from . import sms_notification diff --git a/sms_939/models/sms_hook.py b/sms_939/models/sms_hook.py new file mode 100644 index 000000000..9be738f44 --- /dev/null +++ b/sms_939/models/sms_hook.py @@ -0,0 +1,45 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# Copyright (C) 2018 Compassion CH (http://www.compassion.ch) +# Releasing children from poverty in Jesus' name +# @author: Emanuel Cino +# +# The licence is in the file __manifest__.py +# +############################################################################## + +import logging + + +from odoo import models, api, fields, _ +from odoo.exceptions import ValidationError + + +logger = logging.getLogger(__name__) + + +class SmsHook(models.Model): + """ A sponsored child """ + _name = 'sms.hook' + _description = 'SMS Keyword Hook' + + name = fields.Char( + 'Keyword service', + help='This hook will be called when a SMS from this registered' + 'service is received.', + required=True + ) + func_name = fields.Char( + 'Function', + help='Method name that will be executed at reception. The method must ' + 'exist in sms.notification object and returns a ' + 'SmsNotificationAnswer object', + required=True + ) + + @api.constrains('func_name') + def check_func_name(self): + if not hasattr(self.env['sms.notification'], self.func_name): + raise ValidationError( + _("The function is not implemented in sms.notification")) diff --git a/sms_939/models/sms_notification.py b/sms_939/models/sms_notification.py new file mode 100644 index 000000000..d009de424 --- /dev/null +++ b/sms_939/models/sms_notification.py @@ -0,0 +1,136 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# Copyright (C) 2018 Compassion CH (http://www.compassion.ch) +# Releasing children from poverty in Jesus' name +# @author: Emanuel Cino +# +# The licence is in the file __manifest__.py +# +############################################################################## + +import logging +import traceback + + +from odoo import models, api, fields, tools, _ + +from ..tools import SmsNotificationAnswer + + +logger = logging.getLogger(__name__) +testing = tools.config.get('test_enable') + + +class SmsNotification(models.Model): + _name = 'sms.notification' + _description = 'SMS Notification' + _order = 'date desc' + + instance = fields.Char() + sender = fields.Char(required=True) + operator = fields.Char() + service = fields.Char(required=True) + hook_id = fields.Many2one('sms.hook', 'Hook') + language = fields.Char() + date = fields.Datetime() + uuid = fields.Char() + text = fields.Char() + state = fields.Selection([ + ('new', 'Received'), + ('success', 'Success'), + ('failed', 'Failed') + ], default='new') + failure_details = fields.Text() + answer = fields.Text() + partner_id = fields.Many2one('res.partner') + + @api.model + def create(self, vals): + # Try to find a matching partner given phone number + phone = vals.get('sender') + partner_obj = self.env['res.partner'] + partner = partner_obj.search([ + ('mobile', 'like', phone) + ]) + if not partner: + partner = partner_obj.search([ + ('phone', 'like', phone) + ]) + if partner and len(partner) == 1: + vals['partner_id'] = partner.id + # Attach the hook configuration + hook = self.env['sms.hook'].search([ + ('name', '=ilike', vals['service'])]) + vals['hook_id'] = hook.id + sms = super(SmsNotification, self).create(vals) + if not testing: + # Directly commit as we don't want to lose SMS in case of failure + self.env.cr.commit() # pylint: disable=invalid-commit + # Return record with language context + lang = self.env['res.lang'].search([('code', '=', sms.language)], + limit=1) + sms = sms.with_context(lang=lang and lang.code or 'en_US') + return sms + + def run_service(self): + """ + Executes a service when receiving a SMS notification from 939. + If the function takes more than 15 seconds, it will fail and return + an error to the sender (to avoid generic error being sent from 939) + :param params: All request parameters received from 939 web service + :return: werkzeug.Response object + """ + self.ensure_one() + if not self.hook_id: + hooks = self.env['sms.hook'].search([]) + sms_answer = SmsNotificationAnswer(_( + "Sorry, we could not understand your request. " + "Supported services are :\n - %s " + ) % "\n- ".join(hooks.mapped('name'))) + self.write({ + 'state': 'failed', + 'failure_details': 'Service is not implemented. ' + 'Please configure a hook for this service.', + 'answer': sms_answer.xml_message + }) + return sms_answer.get_answer() + service = getattr(self, self.hook_id.func_name) + try: + sms_answer = service() + self.write({ + 'state': 'success', + 'answer': sms_answer.xml_message + }) + except Exception: + # Abort pending operations + self.env.cr.rollback() + self.env.invalidate_all() + logger.error("Error processing SMS service", exc_info=True) + sms_answer = SmsNotificationAnswer(_( + "Sorry, the service is not available at this time. " + "Our team is informed and is currently working on it." + )) + if not testing: + self.write({ + 'state': 'failed', + 'failure_details': traceback.format_exc(), + 'answer': sms_answer.xml_message + }) + return sms_answer.get_answer() + + def sponsor_service(self): + self.ensure_one() + # Create a sms child request + child_request = self.env['sms.child.request'].create({ + 'sender': self.sender, + }) + return SmsNotificationAnswer( + _("Thank you for your will to help a child ! \n" + "You can release a child from poverty today by clicking on this " + "link: %s") % child_request.full_url + ) + + def test_service(self): + self.ensure_one() + return SmsNotificationAnswer(_("Thanks!")) diff --git a/sms_939/readme/CONFIGURE.rst b/sms_939/readme/CONFIGURE.rst new file mode 100644 index 000000000..f289b2836 --- /dev/null +++ b/sms_939/readme/CONFIGURE.rst @@ -0,0 +1,4 @@ +Make sure you have setup your 939 account in your odoo.conf file + +#. 939_username = XXX +#. 939_password = YYY diff --git a/sms_939/readme/CONTRIBUTORS.rst b/sms_939/readme/CONTRIBUTORS.rst new file mode 100644 index 000000000..6a9ca3183 --- /dev/null +++ b/sms_939/readme/CONTRIBUTORS.rst @@ -0,0 +1 @@ +* Emanuel Cino diff --git a/sms_939/readme/DESCRIPTION.rst b/sms_939/readme/DESCRIPTION.rst new file mode 100644 index 000000000..2a204e61b --- /dev/null +++ b/sms_939/readme/DESCRIPTION.rst @@ -0,0 +1,3 @@ +This module integrates the services of 939_ into Odoo. It can send SMS messages and receive SMS notifications. + +.. _939: http://www.939.ch/ diff --git a/sms_939/readme/HISTORY.rst b/sms_939/readme/HISTORY.rst new file mode 100644 index 000000000..e13236d77 --- /dev/null +++ b/sms_939/readme/HISTORY.rst @@ -0,0 +1,4 @@ +10.0.1.0.0 (2018-07-09) +~~~~~~~~~~~~~~~~~~~~~~~ + +* [ADD] Add the module diff --git a/sms_939/readme/ROADMAP.rst b/sms_939/readme/ROADMAP.rst new file mode 100644 index 000000000..75f0118cf --- /dev/null +++ b/sms_939/readme/ROADMAP.rst @@ -0,0 +1 @@ +* Nothing yet diff --git a/sms_939/readme/USAGE.rst b/sms_939/readme/USAGE.rst new file mode 100644 index 000000000..d0b42cad1 --- /dev/null +++ b/sms_939/readme/USAGE.rst @@ -0,0 +1,3 @@ +To use this module, you need to: + +#. Have a mobile app that can send messages to your Odoo instance diff --git a/sms_939/security/ir.model.access.csv b/sms_939/security/ir.model.access.csv new file mode 100644 index 000000000..7214abbb1 --- /dev/null +++ b/sms_939/security/ir.model.access.csv @@ -0,0 +1,3 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +sms_notification_full,SMS Notification full access,model_sms_notification,base.group_system,1,1,1,1 +sms_hook_full,SMS hook full access,model_sms_hook,base.group_system,1,1,1,1 diff --git a/sms_939/tests/__init__.py b/sms_939/tests/__init__.py new file mode 100644 index 000000000..963fec35e --- /dev/null +++ b/sms_939/tests/__init__.py @@ -0,0 +1,12 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# Copyright (C) 2018 Compassion CH (http://www.compassion.ch) +# Releasing children from poverty in Jesus' name +# @author: Emanuel Cino +# +# The licence is in the file __manifest__.py +# +############################################################################## + +from . import test_sms_notification diff --git a/sms_939/tests/test_sms_notification.py b/sms_939/tests/test_sms_notification.py new file mode 100644 index 000000000..37722696e --- /dev/null +++ b/sms_939/tests/test_sms_notification.py @@ -0,0 +1,63 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# Copyright (C) 2018 Compassion CH (http://www.compassion.ch) +# Releasing children from poverty in Jesus' name +# @author: Emanuel Cino +# +# The licence is in the file __manifest__.py +# +############################################################################## +import logging +import urllib +from odoo.fields import Datetime +from odoo.tests import HttpCase + +_logger = logging.getLogger(__name__) + + +class TestMobileAppConnector(HttpCase): + + def _send_sms_notification(self, params, send_mode='direct'): + uuid = 'uid-1232389' + params.update({ + 'instance': 'test', + 'sender': '+41414104141', + 'operator': 'orange', + 'command': 'FORWARD', + 'date': Datetime.now(), + 'uuid': uuid, + }) + if send_mode != 'direct': + params.update({ + 'receptionDate': Datetime.now(), + 'requestUid': uuid, + }) + url_params = urllib.urlencode(params) + response = self.url_open('/sms/939/?' + url_params) + response_str = response.read() + return response_str + + notification = self.env['sms.notification'].create(params) + self.assertEqual(notification.state, 'new') + response = notification.run_service() + response_str = response.data + self.assertEqual(notification.answer, response_str) + return notification + + def test_controller(self): + response = self._send_sms_notification({ + 'service': 'test', + 'language': 'fr', + 'text': 'This is a test' + }, send_mode='request') + self.assertTrue('Thanks!' in response) + + def test_basic_service(self): + notification = self._send_sms_notification({ + 'service': 'test', + 'language': 'fr', + 'text': 'This is a test' + }) + self.assertEqual(notification.state, 'success') + self.assertTrue('Thanks!' in notification.answer) diff --git a/sms_939/tools/__init__.py b/sms_939/tools/__init__.py new file mode 100644 index 000000000..d1cecc138 --- /dev/null +++ b/sms_939/tools/__init__.py @@ -0,0 +1,12 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# Copyright (C) 2018 Compassion CH (http://www.compassion.ch) +# Releasing children from poverty in Jesus' name +# @author: Emanuel Cino +# +# The licence is in the file __manifest__.py +# +############################################################################## + +from .sms_939_service import * diff --git a/sms_939/tools/sms_939_service.py b/sms_939/tools/sms_939_service.py new file mode 100644 index 000000000..d65e1610f --- /dev/null +++ b/sms_939/tools/sms_939_service.py @@ -0,0 +1,74 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# Copyright (C) 2016 Compassion CH (http://www.compassion.ch) +# Releasing children from poverty in Jesus' name +# @author: Emanuel Cino +# +# The licence is in the file __manifest__.py +# +############################################################################## +from io import BytesIO +from werkzeug.wrappers import Response + + +try: + import xml.etree.cElementTree as etree +except ImportError: + import xml.etree.ElementTree as etree + + +def _sanitize_message(message): + return message.replace('&', '&')\ + .replace('&', '&')\ + .replace('<', '<')\ + .replace('>', '>')\ + .replace('\'', ''')\ + .replace('"', '"') + + +class SmsNotificationAnswer(object): + def __init__(self, messages, costs=None, maxSMSsize=0): + """ + :param messages: list of text messages to return to the sender + :param costs: optional list of costs of the text messages + :param maxSMSsize: optional maximal amount of split messages + """ + if costs and len(costs) != len(messages): + raise ValueError("Costs must be defined for each message sent.") + if messages is None or not isinstance(messages, (list, basestring)): + raise ValueError("You must give at least one message") + if isinstance(messages, basestring): + messages = [messages] + self.messages = messages + self.costs = costs + self.maxSMSsize = maxSMSsize + self.xml_message = self._get_xml() + + def __str__(self): + return self.xml_message + + def _get_xml(self): + # Generates XML Formatted message for 939 service + document = etree.Element('NotificationReply') + for index, message in enumerate(self.messages): + mess_node = etree.SubElement(document, 'message') + etree.SubElement(mess_node, 'text').text = _sanitize_message( + message) + if self.costs: + etree.SubElement(mess_node, 'cost').text = self.costs[index] + if self.maxSMSsize: + etree.SubElement(mess_node, 'maximumSMSAmount').text = \ + self.maxSMSsize + + xml_buffer = BytesIO() + et = etree.ElementTree(document) + et.write(xml_buffer, encoding='utf-8', xml_declaration=True) + return xml_buffer.getvalue() + + def get_answer(self): + """ + Wraps messages in an XML Response compatible with 939 API. + :return: werkzeug.Response object + """ + return Response(self.xml_message, content_type='text/xml') diff --git a/sms_939/views/sms_views.xml b/sms_939/views/sms_views.xml new file mode 100644 index 000000000..37722f74c --- /dev/null +++ b/sms_939/views/sms_views.xml @@ -0,0 +1,93 @@ + + + sms.hook.tree + sms.hook + + + + + + + + + + sms.notification.tree + sms.notification + + + + + + + + + + + + + + + + sms.notification.form + sms.notification + +
+
+ +
+ + + + + + + + + + + + + + + + + + + + +
+
+
+ + sms.notification.search + sms.notification + + + + + + + + + + + + + SMS Requests + ir.actions.act_window + sms.notification + form + tree,form + + + SMS Hooks + ir.actions.act_window + sms.hook + form + tree,form + + + + + +
diff --git a/sponsorship_switzerland/README.rst b/sponsorship_switzerland/README.rst index b88fd93ee..6f573c741 100644 --- a/sponsorship_switzerland/README.rst +++ b/sponsorship_switzerland/README.rst @@ -1,28 +1,28 @@ -.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg - :alt: License: AGPL-3 - -Tailor Sponsorships to Compassion CH needs -========================================== - -This module adds specific behaviours of sponsorships for Switzerland. - -* Statement completion rules with invoice generation. -* Waiting welcome state for sending welcome letters -* Payment modes for sponsorships : LSV/DD and other -* New validation workflow with waiting mandate state for LSV/DD contracts -* Add some useful statistics about sponsorships - -Credits -======= - -Contributors ------------- - -* Emanuel Cino -* Steve Ferry -* Sebastien Toth - -Maintainer ----------- - -This module is maintained by `Compassion Switzerland `. +.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg + :alt: License: AGPL-3 + +Tailor Sponsorships to Compassion CH needs +========================================== + +This module adds specific behaviours of sponsorships for Switzerland. + +* Statement completion rules with invoice generation. +* Waiting welcome state for sending welcome letters +* Payment modes for sponsorships : LSV/DD and other +* New validation workflow with waiting mandate state for LSV/DD contracts +* Add some useful statistics about sponsorships + +Credits +======= + +Contributors +------------ + +* Emanuel Cino +* Steve Ferry +* Sebastien Toth + +Maintainer +---------- + +This module is maintained by `Compassion Switzerland `. diff --git a/sponsorship_switzerland/__init__.py b/sponsorship_switzerland/__init__.py index 17494a862..752c57bde 100644 --- a/sponsorship_switzerland/__init__.py +++ b/sponsorship_switzerland/__init__.py @@ -1,14 +1,14 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# Copyright (C) 2015 Compassion CH (http://www.compassion.ch) -# Releasing children from poverty in Jesus' name -# @author: Emanuel Cino -# -# The licence is in the file __manifest__.py -# -############################################################################## - -from . import models -from . import wizards -from . import reports +# -*- coding: utf-8 -*- +############################################################################## +# +# Copyright (C) 2015 Compassion CH (http://www.compassion.ch) +# Releasing children from poverty in Jesus' name +# @author: Emanuel Cino +# +# The licence is in the file __manifest__.py +# +############################################################################## + +from . import models +from . import wizards +from . import reports diff --git a/sponsorship_switzerland/models/__init__.py b/sponsorship_switzerland/models/__init__.py index b98dd8789..6636005d2 100644 --- a/sponsorship_switzerland/models/__init__.py +++ b/sponsorship_switzerland/models/__init__.py @@ -1,19 +1,19 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# Copyright (C) 2015 Compassion CH (http://www.compassion.ch) -# Releasing children from poverty in Jesus' name -# @author: Emanuel Cino -# -# The licence is in the file __manifest__.py -# -############################################################################## - -from . import contracts -from . import account_invoice -from . import res_partner_category -from . import completion_rules -from . import account_banking_mandate -from . import contract_group -from . import gift_compassion -from . import queue_job +# -*- coding: utf-8 -*- +############################################################################## +# +# Copyright (C) 2015 Compassion CH (http://www.compassion.ch) +# Releasing children from poverty in Jesus' name +# @author: Emanuel Cino +# +# The licence is in the file __manifest__.py +# +############################################################################## + +from . import contracts +from . import account_invoice +from . import res_partner_category +from . import completion_rules +from . import account_banking_mandate +from . import contract_group +from . import gift_compassion +from . import queue_job diff --git a/sponsorship_switzerland/models/account_invoice.py b/sponsorship_switzerland/models/account_invoice.py index 426934b54..a42bb400b 100644 --- a/sponsorship_switzerland/models/account_invoice.py +++ b/sponsorship_switzerland/models/account_invoice.py @@ -14,35 +14,14 @@ from odoo.tools import mod10r from odoo.addons.queue_job.job import job, related_action from odoo.addons.sponsorship_compassion.models.product import \ - GIFT_CATEGORY, SPONSORSHIP_CATEGORY, FUND_CATEGORY + GIFT_CATEGORY, SPONSORSHIP_CATEGORY class AccountInvoice(models.Model): _inherit = 'account.invoice' - invoice_type = fields.Selection([ - ('sponsorship', 'Sponsorship'), - ('gift', 'Gift'), - ('fund', 'Fund donation'), - ('other', 'Other'), - ], compute='_compute_invoice_type', store=True) unrec_items = fields.Integer(compute='_compute_unrec_items') - @api.depends('invoice_line_ids', 'state') - @api.multi - def _compute_invoice_type(self): - for invoice in self.filtered(lambda i: i.state in ('open', 'paid')): - categories = invoice.with_context(lang='en_US').mapped( - 'invoice_line_ids.product_id.categ_name') - if SPONSORSHIP_CATEGORY in categories: - invoice.invoice_type = 'sponsorship' - elif GIFT_CATEGORY in categories: - invoice.invoice_type = 'gift' - elif FUND_CATEGORY in categories: - invoice.invoice_type = 'fund' - else: - invoice.invoice_type = 'other' - @api.multi def _compute_unrec_items(self): move_line_obj = self.env['account.move.line'] diff --git a/sponsorship_switzerland/views/account_invoice_view.xml b/sponsorship_switzerland/views/account_invoice_view.xml index 0bea9a63a..6ca554a88 100644 --- a/sponsorship_switzerland/views/account_invoice_view.xml +++ b/sponsorship_switzerland/views/account_invoice_view.xml @@ -3,7 +3,7 @@ account.invoice.tree.compassion.ch account.invoice - + @@ -15,26 +15,6 @@ - - - - - - - - - account.invoice.sponsorship.filter - account.invoice - - - - - - - - - - diff --git a/travis/compassion_class_structure.txt b/travis/compassion_class_structure.txt index 8173a0310..ee9fedb06 100644 --- a/travis/compassion_class_structure.txt +++ b/travis/compassion_class_structure.txt @@ -1,27 +1,27 @@ - ########################################################################## - # FIELDS # - ########################################################################## - - ########################################################################## - # FIELDS METHODS # - ########################################################################## - - ########################################################################## - # ORM METHODS # - ########################################################################## - - ########################################################################## - # PUBLIC METHODS # - ########################################################################## - - ########################################################################## - # VIEW CALLBACKS # - ########################################################################## - - ########################################################################## - # WORKFLOW METHODS # - ########################################################################## - - ########################################################################## - # PRIVATE METHODS # + ########################################################################## + # FIELDS # + ########################################################################## + + ########################################################################## + # FIELDS METHODS # + ########################################################################## + + ########################################################################## + # ORM METHODS # + ########################################################################## + + ########################################################################## + # PUBLIC METHODS # + ########################################################################## + + ########################################################################## + # VIEW CALLBACKS # + ########################################################################## + + ########################################################################## + # WORKFLOW METHODS # + ########################################################################## + + ########################################################################## + # PRIVATE METHODS # ########################################################################## \ No newline at end of file diff --git a/travis/compassion_openerp_header.txt b/travis/compassion_openerp_header.txt index f3d08bded..df5169d4f 100644 --- a/travis/compassion_openerp_header.txt +++ b/travis/compassion_openerp_header.txt @@ -1,28 +1,28 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# ______ Releasing children from poverty _ -# / ____/___ ____ ___ ____ ____ ___________(_)___ ____ -# / / / __ \/ __ `__ \/ __ \/ __ `/ ___/ ___/ / __ \/ __ \ -# / /___/ /_/ / / / / / / /_/ / /_/ (__ |__ ) / /_/ / / / / -# \____/\____/_/ /_/ /_/ .___/\__,_/____/____/_/\____/_/ /_/ -# /_/ -# in Jesus' name -# -# Copyright (C) 2014 Compassion CH (http://www.compassion.ch) -# @author: [[ NAME ]] [[]] -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -############################################################################## +# -*- coding: utf-8 -*- +############################################################################## +# +# ______ Releasing children from poverty _ +# / ____/___ ____ ___ ____ ____ ___________(_)___ ____ +# / / / __ \/ __ `__ \/ __ \/ __ `/ ___/ ___/ / __ \/ __ \ +# / /___/ /_/ / / / / / / /_/ / /_/ (__ |__ ) / /_/ / / / / +# \____/\____/_/ /_/ /_/ .___/\__,_/____/____/_/\____/_/ /_/ +# /_/ +# in Jesus' name +# +# Copyright (C) 2014 Compassion CH (http://www.compassion.ch) +# @author: [[ NAME ]] [[]] +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## diff --git a/travis/compassion_py_header.txt b/travis/compassion_py_header.txt index f45ffa1df..c3896ffb4 100644 --- a/travis/compassion_py_header.txt +++ b/travis/compassion_py_header.txt @@ -1,10 +1,10 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# Copyright (C) 2014 Compassion CH (http://www.compassion.ch) -# Releasing children from poverty in Jesus' name -# @author: [[ NAME ]] [[]] -# -# The licence is in the file __manifest__.py -# -############################################################################## +# -*- coding: utf-8 -*- +############################################################################## +# +# Copyright (C) 2014 Compassion CH (http://www.compassion.ch) +# Releasing children from poverty in Jesus' name +# @author: [[ NAME ]] [[]] +# +# The licence is in the file __manifest__.py +# +##############################################################################