From 738395d5dc6624e953d438d9c7c72510110e52fe Mon Sep 17 00:00:00 2001 From: root Date: Tue, 31 May 2022 21:35:07 +0200 Subject: [PATCH] adhesion --- __manifest__.py | 6 +- controllers/kalachakra.py | 282 +++++++++++++++++------------- i18n/fr.po | 159 ++++++++++++++--- models/__init__.py | 5 +- models/donation.py | 25 +-- models/event.py | 34 ++-- models/membership.py | 140 +++++++++++++++ models/partner.py | 24 ++- models/payment_transaction.py | 48 +++++ models/product.py | 13 ++ security/ir.model.access.csv | 3 + static/description/membership.png | Bin 0 -> 26294 bytes views/event.xml | 5 +- views/membership.xml | 52 ++++++ views/partner.xml | 52 +----- views/product.xml | 17 ++ views/website_participation.xml | 5 + 17 files changed, 631 insertions(+), 239 deletions(-) create mode 100644 models/membership.py create mode 100644 models/payment_transaction.py create mode 100644 models/product.py create mode 100644 static/description/membership.png create mode 100644 views/membership.xml create mode 100644 views/product.xml diff --git a/__manifest__.py b/__manifest__.py index 47bed23..7cc89aa 100644 --- a/__manifest__.py +++ b/__manifest__.py @@ -29,15 +29,17 @@ 'views/templates.xml', 'views/calendar.xml', 'views/contactus.xml', - # 'views/partner.xml', + 'views/partner.xml', # 'views/partnerimport.xml', 'report/report_donationtax.xml', 'report/report.xml', 'views/website_participation.xml', + 'views/product.xml', #'report/report_donationtax.xml', #'report/report.xml' 'views/event.xml', - 'views/event_templates_list.xml' + 'views/event_templates_list.xml', + 'views/membership.xml' ], # only loaded in demonstration mode diff --git a/controllers/kalachakra.py b/controllers/kalachakra.py index 3c97355..546117c 100644 --- a/controllers/kalachakra.py +++ b/controllers/kalachakra.py @@ -4,6 +4,7 @@ from odoo.http import request import werkzeug from datetime import datetime from odoo.tools import format_datetime, format_date, is_html_empty +from dateutil.relativedelta import relativedelta from odoo.exceptions import UserError from odoo.addons.website_event.controllers.main import WebsiteEventController from odoo.addons.payment.controllers.portal import PaymentProcessing @@ -89,13 +90,24 @@ class kalachakra_event(WebsiteEventController,PaymentProcessing): @http.route(['/kalachakra/participation'], type='http', auth='public', website=True, sitemap=False,csrf=False) def participation(self,type=None,**post): data={} + request.session['kalachakra_transaction']='' if type=='donation': - request.session['donation_transaction']=True + request.session['kalachakra_transaction']='donation' + data['kalachakra_transaction']='donation' data['title']='make donation' data['amount']=10 #donation product data['products']=request.env['product.product'].search([('donation','=',True)]) + if type=='membership': + request.session['kalachakra_transaction']='membership' + data['kalachakra_transaction']='membership' + data['title']='Become a member' + p=request.env['product.template'].search([('membership_product','=',True)]) + if not p: raise UserError(_('No membership product, please add one')) + data['amount']=p.list_price + #membership product + data['products']=p userid=request.env.context.get('uid') @@ -137,14 +149,21 @@ class kalachakra_event(WebsiteEventController,PaymentProcessing): data['partner']=partner - - - - return http.request.render('kalachakra.participation_form',data) @http.route(['/kalachakra/payment_choice'], type='http', auth='public', website=True, sitemap=False,csrf=False) def payment_choice(self,**post): + + #gestion du retour page précédente depuis page de paiement + if not post.get('product_id'): + if request.session['kalachakra_transaction']=='donation': + request.env['donation.donation'].search(['id','=',int(request.session['donation_id'])]).unlink() + if request.session['kalachakra_transaction']=='membership': + request.env['kalachakra.membership'].search(['id','=',int(request.session['membership_id'])]).unlink() + + return request.redirect('/kalachakra/participation?type='+request.session['kalachakra_transaction']) + + userid=request.env.context.get('uid') if userid: user=request.env['res.users'].search([('id','=',int(userid))]) @@ -170,11 +189,15 @@ class kalachakra_event(WebsiteEventController,PaymentProcessing): data={} - data['submit_txt']='Donate now' + if request.session['kalachakra_transaction']=='donation': + data['submit_txt']='Donate now' + if request.session['kalachakra_transaction'] in ['membership'] : + data['submit_txt']='Pay now' + data['success_url']='/kalachakra/payment/success' data['error_url']='/kalachakra/payment/error' - if request.session.get('donation_transaction')==True: + if request.session['kalachakra_transaction'] in ['donation','membership']: data['acquirers'] = list(request.env['payment.acquirer'].search([ ('state', 'in', ['enabled', 'test']), ('company_id', '=', request.env.company.id), @@ -195,57 +218,48 @@ class kalachakra_event(WebsiteEventController,PaymentProcessing): data['payment_tokens']=payment_tokens #create donation - vals={} - vals['partner_id']=partner.id - vals['donation_date']=datetime.now() - vals['tax_receipt_option']='annual' - #mode de paiement CB - electronic_method=request.env['account.payment.method'].search([('code','=','electronic')],limit=1) - if electronic_method: - cb_mode=request.env['account.payment.mode'].search([('payment_method_id','=',int(electronic_method.id))],limit=1) - if cb_mode: - vals['payment_mode_id']=cb_mode.id - else: - raise Warning('please configure credit card mode') - vals['state']='draft' - vals['payment_ref']='internet' - donation_draft=request.env['donation.donation'].sudo().create(vals) - vals={} - #create line donation - vals['donation_id']=donation_draft.id - product=request.env['product.product'].search([('id','=',int(post.get('product_id')))]) - vals['product_id']=int(post.get('product_id')) - vals['display_name']=product.name - vals['quantity']=1 - vals['unit_price']=post.get('amount') - vals['tax_receipt_ok']=product.tax_receipt_ok - - - donation_line=request.env['donation.line'].sudo().create(vals) - - - #create sale order - # vals={} - - - # vals['partner_id']=partner.id - # order_draft=request.env['sale.order'].sudo().create(vals) - # data['order_id']=order_draft.id - # #création des lignes de devis - # vals={} - # vals['order_id']=order_draft.id - # #ajout du produit - # vals['product_id']=int(post.get('product_id')) - # vals['product_uom_qty']=1 - # vals['price_unit']=post.get('amount') - - # product=request.env['product.product'].search([('id','=',int(post.get('product_id')))]) - # vals['name']=product.name - - # order_line=request.env['sale.order.line'].sudo().create(vals) + if request.session['kalachakra_transaction'] in ['donation']: + vals={} + vals['partner_id']=partner.id + vals['donation_date']=datetime.now() + vals['tax_receipt_option']='annual' + #mode de paiement CB + electronic_method=request.env['account.payment.method'].search([('code','=','electronic')],limit=1) + if electronic_method: + cb_mode=request.env['account.payment.mode'].search([('payment_method_id','=',int(electronic_method.id))],limit=1) + if cb_mode: + vals['payment_mode_id']=cb_mode.id + else: + raise Warning('please configure credit card mode') + vals['state']='draft' + vals['payment_ref']='internet' + donation_draft=request.env['donation.donation'].sudo().create(vals) + vals={} + #create line donation + vals['donation_id']=donation_draft.id + product=request.env['product.product'].search([('id','=',int(post.get('product_id')))]) + vals['product_id']=int(post.get('product_id')) + vals['display_name']=product.name + vals['quantity']=1 + vals['unit_price']=post.get('amount') + vals['tax_receipt_ok']=product.tax_receipt_ok + + + donation_line=request.env['donation.line'].sudo().create(vals) + + data['order_id']=donation_draft.id + request.session['donation_id'] = donation_draft.id - data['order_id']=donation_draft.id - request.session['donation_id'] = donation_draft.id + #create membership + if request.session['kalachakra_transaction'] in ['membership']: + vals={} + vals['partner_id']=partner.id + vals['product_id']=int(post.get('product_id')) + vals['start_date']=datetime.now() + vals['end_date']=datetime.now()+relativedelta(years=1) + membership=request.env['kalachakra.membership'].sudo().create(vals) + data['order_id']=membership.id + request.session['membership_id'] = membership.id return http.request.render('kalachakra.payment_choice_form',data) @@ -263,7 +277,10 @@ class kalachakra_event(WebsiteEventController,PaymentProcessing): user is redirected to the checkout page """ # Ensure a payment acquirer is selected - donation_id=order_id + + + + if not acquirer_id: return False @@ -272,37 +289,71 @@ class kalachakra_event(WebsiteEventController,PaymentProcessing): except: return False + if request.session['kalachakra_transaction'] in ['donation']: + # Retrieve the donation + donation_id=order_id + donation=request.env['donation.donation'].search([('id','=',int(donation_id))], limit=1) - # Retrieve the donation - donation=request.env['donation.donation'].search([('id','=',int(donation_id))], limit=1) - - # Ensure there is something to proceed - if not donation or (donation and not donation.line_ids): - return False + # Ensure there is something to proceed + if not donation or (donation and not donation.line_ids): + return False + + assert donation.partner_id.id != request.website.partner_id.id + + # Create transaction + vals = {'acquirer_id': acquirer_id, + 'return_url': '/kalachakra/payment/validate'} + + if save_token: + vals['type'] = 'form_save' + if token: + vals['payment_token_id'] = int(token) + + transaction = donation._create_payment_transaction(vals) + + # store the new transaction into the transaction list and if there's an old one, we remove it + # until the day the ecommerce supports multiple orders at the same time + last_tx_id = request.session.get('__website_sale_last_tx_id') + last_tx = request.env['payment.transaction'].browse(last_tx_id).sudo().exists() + if last_tx: + PaymentProcessing.remove_payment_transaction(last_tx) + PaymentProcessing.add_payment_transaction(transaction) + request.session['__website_sale_last_tx_id'] = transaction.id + return transaction.render_donation_button(donation) + + if request.session['kalachakra_transaction'] in ['membership']: + # Retrieve the donation + membership_id=order_id + membership=request.env['kalachakra.membership'].search([('id','=',int(membership_id))], limit=1) - assert donation.partner_id.id != request.website.partner_id.id - # Create transaction - vals = {'acquirer_id': acquirer_id, - 'return_url': '/kalachakra/payment/validate'} - - if save_token: - vals['type'] = 'form_save' - if token: - vals['payment_token_id'] = int(token) - - transaction = donation._create_payment_transaction(vals) - - # store the new transaction into the transaction list and if there's an old one, we remove it - # until the day the ecommerce supports multiple orders at the same time - last_tx_id = request.session.get('__website_sale_last_tx_id') - last_tx = request.env['payment.transaction'].browse(last_tx_id).sudo().exists() - if last_tx: - PaymentProcessing.remove_payment_transaction(last_tx) - PaymentProcessing.add_payment_transaction(transaction) - request.session['__website_sale_last_tx_id'] = transaction.id - return transaction.render_donation_button(donation) + # Ensure there is something to proceed + if not membership : + return False + + assert membership.partner_id.id != request.website.partner_id.id + + # Create transaction + vals = {'acquirer_id': acquirer_id, + 'return_url': '/kalachakra/payment/validate'} + + if save_token: + vals['type'] = 'form_save' + if token: + vals['payment_token_id'] = int(token) + + transaction = membership._create_payment_transaction(vals) + + # store the new transaction into the transaction list and if there's an old one, we remove it + # until the day the ecommerce supports multiple orders at the same time + last_tx_id = request.session.get('__website_sale_last_tx_id') + last_tx = request.env['payment.transaction'].browse(last_tx_id).sudo().exists() + if last_tx: + PaymentProcessing.remove_payment_transaction(last_tx) + PaymentProcessing.add_payment_transaction(transaction) + request.session['__website_sale_last_tx_id'] = transaction.id + return transaction.render_membership_button(membership) http.route('/kalachakra/payment/token', type='http', auth='public', website=True, sitemap=False) def kalachakra_payment_token(self, pm_id=None, **kwargs): @@ -323,7 +374,12 @@ class kalachakra_event(WebsiteEventController,PaymentProcessing): # Create transaction vals = {'payment_token_id': pm_id, 'return_url': '/kalachakra/payment/validate'} - tx = donation._create_payment_transaction(vals) + if request.session['kalachakra_transaction'] in ['donation']: + tx = donation._create_payment_transaction(vals) + + if request.session['kalachakra_transaction'] in ['membership']: + tx = membership._create_payment_transaction(vals) + request.session['transaction_id']=tx.id PaymentProcessing.add_payment_transaction(tx) return request.redirect('kalachakra/payment/process') @@ -347,11 +403,20 @@ class kalachakra_event(WebsiteEventController,PaymentProcessing): - UDPATE ME """ - donation_id=int(request.session['donation_id']) - #validation du don car la tranasction est ok - donation=request.env['donation.donation'].search([('id','=',donation_id)]) - donation.state='done' - request.session['donation_id']=None + if request.session['kalachakra_transaction'] in ['donation']: + + donation_id=int(request.session['donation_id']) + #validation du don car la tranasction est ok + donation=request.env['donation.donation'].search([('id','=',donation_id)]) + donation.state='done' + request.session['donation_id']=None + + if request.session['kalachakra_transaction'] in ['membership']: + membership_id=int(request.session['membership_id']) + #validation du don car la tranasction est ok + membership=request.env['kalachakra.membership'].search([('id','=',membership_id)]) + membership.state='done' + request.session['membership_id']=None transaction_id=int(request.session['__website_sale_last_tx_id'] ) tx = request.env['payment.transaction'].sudo().browse(transaction_id) @@ -362,43 +427,10 @@ class kalachakra_event(WebsiteEventController,PaymentProcessing): PaymentProcessing.remove_payment_transaction(tx) return request.redirect('/kalachakra/payment/confirmation') - # if sale_order_id is None: - # order = request.website.sale_get_order() - # else: - # order = request.env['sale.order'].sudo().browse(sale_order_id) - # assert order.id == request.session.get('sale_last_order_id') - - # if transaction_id: - # tx = request.env['payment.transaction'].sudo().browse(transaction_id) - # assert tx in order.transaction_ids() - # elif order: - # tx = order.get_portal_last_transaction() - # else: - # tx = None - - # if not order or (order.amount_total and not tx): - # return request.redirect('/kalachakra/ma') - - # if order and not order.amount_total and not tx: - # order.with_context(send_email=True).action_confirm() - # return request.redirect(order.get_portal_url()) - - # # clean context and session, then redirect to the confirmation page - # request.website.sale_reset() - # if tx and tx.state == 'draft': - # return request.redirect('/kalachakra/') - - # PaymentProcessing.remove_payment_transaction(tx) - # return request.redirect('/kalachakra/payment/confirmation') @http.route(['/kalachakra/payment/confirmation'], type='http', auth="public", website=True, sitemap=False) def kalachakra_payment_confirmation(self, **post): return request.render("kalachakra.thankyou") - # sale_order_id = request.session.get('sale_last_order_id') - # if sale_order_id: - # order = request.env['sale.order'].sudo().browse(sale_order_id) - # return request.render("website_sale.confirmation", {'order': order}) - # else: - # return request.redirect('/shop') \ No newline at end of file + \ No newline at end of file diff --git a/i18n/fr.po b/i18n/fr.po index e78f3b7..4c3a966 100644 --- a/i18n/fr.po +++ b/i18n/fr.po @@ -6,8 +6,8 @@ msgid "" msgstr "" "Project-Id-Version: Odoo Server 14.0-20210413\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2022-05-30 12:54+0000\n" -"PO-Revision-Date: 2022-05-30 12:54+0000\n" +"POT-Creation-Date: 2022-05-31 13:07+0000\n" +"PO-Revision-Date: 2022-05-31 13:07+0000\n" "Last-Translator: \n" "Language-Team: \n" "MIME-Version: 1.0\n" @@ -18,7 +18,7 @@ msgstr "" #. module: kalachakra #: model_terms:ir.ui.view,arch_db:kalachakra.kalachakra_template_donation_tax_receipt msgid "5 Passage Delessert – 75010 PARIS" -msgstr "5 Passage Delessert – 75010 PARIS"" +msgstr "" #. module: kalachakra #: model_terms:ir.ui.view,arch_db:kalachakra.kalachakra_template_donation_tax_receipt @@ -30,6 +30,13 @@ msgstr "" msgid "Paris, le {{DATE}}" msgstr "" +#. module: kalachakra +#: model_terms:ir.ui.view,arch_db:kalachakra.event_category_tag +msgid "" +"\n" +" tags" +msgstr "" + #. module: kalachakra #: model_terms:ir.ui.view,arch_db:kalachakra.kalachakra_template_donation_tax_receipt msgid "" @@ -39,18 +46,21 @@ msgstr "" #. module: kalachakra #: code:addons/kalachakra/models/donation.py:0 +#: code:addons/kalachakra/models/membership.py:0 #, python-format msgid "A journal must be specified for the acquirer %s." msgstr "" #. module: kalachakra #: code:addons/kalachakra/models/donation.py:0 +#: code:addons/kalachakra/models/membership.py:0 #, python-format msgid "A payment acquirer is required to create a transaction." msgstr "" #. module: kalachakra #: code:addons/kalachakra/models/donation.py:0 +#: code:addons/kalachakra/models/membership.py:0 #, python-format msgid "" "A transaction can't be linked to sales orders having different partners." @@ -66,6 +76,11 @@ msgstr "Ajouter l'événement au calendrier google" msgid "Affectation *" msgstr "" +#. module: kalachakra +#: model:ir.model.fields,field_description:kalachakra.field_kalachakra_membership__amount +msgid "Amount" +msgstr "Montant" + #. module: kalachakra #: model_terms:ir.ui.view,arch_db:kalachakra.participation_form msgid "Amount (€)" @@ -89,7 +104,7 @@ msgstr "" #. module: kalachakra #: model_terms:ir.ui.view,arch_db:kalachakra.kalachakra_template_donation_tax_receipt msgid "Associationnn Cultuelle Kalachakra – Roue de la Vie" -msgstr "Association Cultuelle Kalachakra – Roue de la Vie" +msgstr "" #. module: kalachakra #: model:ir.model.fields,field_description:kalachakra.field_kalachakra_partnerdraftfile__file @@ -104,9 +119,10 @@ msgstr "" #. module: kalachakra #: model_terms:ir.ui.view,arch_db:kalachakra.participation_form msgid "Country *" -msgstr "Pays *" +msgstr "" #. module: kalachakra +#: model:ir.model.fields,field_description:kalachakra.field_kalachakra_membership__create_uid #: model:ir.model.fields,field_description:kalachakra.field_kalachakra_partnerdraft__create_uid #: model:ir.model.fields,field_description:kalachakra.field_kalachakra_partnerdraftfile__create_uid #: model:ir.model.fields,field_description:kalachakra.field_kalachakra_partnerdraftfile_mapping__create_uid @@ -114,11 +130,17 @@ msgid "Created by" msgstr "" #. module: kalachakra +#: model:ir.model.fields,field_description:kalachakra.field_kalachakra_membership__create_date #: model:ir.model.fields,field_description:kalachakra.field_kalachakra_partnerdraft__create_date #: model:ir.model.fields,field_description:kalachakra.field_kalachakra_partnerdraftfile__create_date #: model:ir.model.fields,field_description:kalachakra.field_kalachakra_partnerdraftfile_mapping__create_date msgid "Created on" -msgstr "" +msgstr "Créé le" + +#. module: kalachakra +#: model:ir.model.fields,field_description:kalachakra.field_kalachakra_membership__currency_id +msgid "Currency" +msgstr "devise" #. module: kalachakra #: model_terms:ir.ui.view,arch_db:kalachakra.kalachakra_template_donation_tax_receipt @@ -129,10 +151,12 @@ msgstr "" #: model:ir.model.fields,field_description:kalachakra.field_donation_donation__display_name #: model:ir.model.fields,field_description:kalachakra.field_event_event__display_name #: model:ir.model.fields,field_description:kalachakra.field_event_tag_category__display_name +#: model:ir.model.fields,field_description:kalachakra.field_kalachakra_membership__display_name #: model:ir.model.fields,field_description:kalachakra.field_kalachakra_partnerdraft__display_name #: model:ir.model.fields,field_description:kalachakra.field_kalachakra_partnerdraftfile__display_name #: model:ir.model.fields,field_description:kalachakra.field_kalachakra_partnerdraftfile_mapping__display_name #: model:ir.model.fields,field_description:kalachakra.field_payment_transaction__display_name +#: model:ir.model.fields,field_description:kalachakra.field_product_template__display_name msgid "Display Name" msgstr "Nom affiché" @@ -169,28 +193,30 @@ msgid "" "Firstname\n" " *" msgstr "" -"Prénom\n" -" *" #. module: kalachakra #: model:ir.model.fields,field_description:kalachakra.field_donation_donation__id #: model:ir.model.fields,field_description:kalachakra.field_event_event__id #: model:ir.model.fields,field_description:kalachakra.field_event_tag_category__id +#: model:ir.model.fields,field_description:kalachakra.field_kalachakra_membership__id #: model:ir.model.fields,field_description:kalachakra.field_kalachakra_partnerdraft__id #: model:ir.model.fields,field_description:kalachakra.field_kalachakra_partnerdraftfile__id #: model:ir.model.fields,field_description:kalachakra.field_kalachakra_partnerdraftfile_mapping__id #: model:ir.model.fields,field_description:kalachakra.field_payment_transaction__id +#: model:ir.model.fields,field_description:kalachakra.field_product_template__id msgid "ID" msgstr "" #. module: kalachakra #: code:addons/kalachakra/models/donation.py:0 +#: code:addons/kalachakra/models/membership.py:0 #, python-format msgid "Invalid token found! Token acquirer %s != %s" msgstr "" #. module: kalachakra #: code:addons/kalachakra/models/donation.py:0 +#: code:addons/kalachakra/models/membership.py:0 #, python-format msgid "Invalid token found! Token partner %s != %s" msgstr "" @@ -199,14 +225,17 @@ msgstr "" #: model:ir.model.fields,field_description:kalachakra.field_donation_donation____last_update #: model:ir.model.fields,field_description:kalachakra.field_event_event____last_update #: model:ir.model.fields,field_description:kalachakra.field_event_tag_category____last_update +#: model:ir.model.fields,field_description:kalachakra.field_kalachakra_membership____last_update #: model:ir.model.fields,field_description:kalachakra.field_kalachakra_partnerdraft____last_update #: model:ir.model.fields,field_description:kalachakra.field_kalachakra_partnerdraftfile____last_update #: model:ir.model.fields,field_description:kalachakra.field_kalachakra_partnerdraftfile_mapping____last_update #: model:ir.model.fields,field_description:kalachakra.field_payment_transaction____last_update +#: model:ir.model.fields,field_description:kalachakra.field_product_template____last_update msgid "Last Modified on" msgstr "Last Updated on" #. module: kalachakra +#: model:ir.model.fields,field_description:kalachakra.field_kalachakra_membership__write_uid #: model:ir.model.fields,field_description:kalachakra.field_kalachakra_partnerdraft__write_uid #: model:ir.model.fields,field_description:kalachakra.field_kalachakra_partnerdraftfile__write_uid #: model:ir.model.fields,field_description:kalachakra.field_kalachakra_partnerdraftfile_mapping__write_uid @@ -214,6 +243,7 @@ msgid "Last Updated by" msgstr "" #. module: kalachakra +#: model:ir.model.fields,field_description:kalachakra.field_kalachakra_membership__write_date #: model:ir.model.fields,field_description:kalachakra.field_kalachakra_partnerdraft__write_date #: model:ir.model.fields,field_description:kalachakra.field_kalachakra_partnerdraftfile__write_date #: model:ir.model.fields,field_description:kalachakra.field_kalachakra_partnerdraftfile_mapping__write_date @@ -237,6 +267,19 @@ msgstr "" msgid "Madame, Monsieur," msgstr "" +#. module: kalachakra +#: model:ir.model.fields,field_description:kalachakra.field_payment_transaction__membership_ids +msgid "Membership" +msgstr "Adhésion" + +#. module: kalachakra +#: model:ir.actions.act_window,name:kalachakra.membership_action +#: model:ir.ui.menu,name:kalachakra.membership_menu +#: model:ir.ui.menu,name:kalachakra.membership_title_menu +#: model:ir.ui.menu,name:kalachakra.membership_top_menu +msgid "Memberships" +msgstr "Adhésions" + #. module: kalachakra #: model_terms:ir.ui.view,arch_db:kalachakra.kalachakra_template_donation_tax_receipt msgid "Mode de versement : Chèque, prélèvement ou espèces" @@ -249,6 +292,12 @@ msgid "" " *" msgstr "" +#. module: kalachakra +#: code:addons/kalachakra/controllers/kalachakra.py:0 +#, python-format +msgid "No membership product, please add one" +msgstr "" + #. module: kalachakra #: model:ir.model.fields,field_description:kalachakra.field_kalachakra_partnerdraftfile__file_name msgid "Nom du fichier" @@ -289,6 +338,8 @@ msgstr "" #. module: kalachakra #: code:addons/kalachakra/models/donation.py:0 +#: code:addons/kalachakra/models/payment_transaction.py:0 +#: code:addons/kalachakra/models/payment_transaction.py:0 #, python-format msgid "Pay Now" msgstr "" @@ -305,24 +356,34 @@ msgstr "Transaction" #. module: kalachakra #: model_terms:ir.ui.view,arch_db:kalachakra.participation_form -msgid "Choix du paiement" +msgid "Payment choice" msgstr "" #. module: kalachakra #: model_terms:ir.ui.view,arch_db:kalachakra.participation_form msgid "Phone" -msgstr "Téléphone" +msgstr "" + +#. module: kalachakra +#: model:ir.model,name:kalachakra.model_product_template +msgid "Product Template" +msgstr "Modèle d'article" #. module: kalachakra #: model_terms:ir.ui.view,arch_db:kalachakra.event_view_form msgid "Remove event from google agenda" -msgstr "Suppriemr l'événement du calendrier google" +msgstr "Supprimer l'événement du calendrier google" #. module: kalachakra #: model_terms:ir.ui.view,arch_db:kalachakra.kalachakra_template_donation_tax_receipt msgid "Reçu de dons aux Œuvres" msgstr "" +#. module: kalachakra +#: model_terms:ir.ui.view,arch_db:kalachakra.view_membership_search +msgid "Search" +msgstr "" + #. module: kalachakra #: model_terms:ir.ui.view,arch_db:kalachakra.kalachakra_template_donation_tax_receipt msgid "Signature
" @@ -339,28 +400,26 @@ msgid "" "Street 1\n" " *" msgstr "" -"Rue 1\n" -" *" #. module: kalachakra #: model_terms:ir.ui.view,arch_db:kalachakra.participation_form msgid "Street 2" -msgstr "Rue 2" +msgstr "" #. module: kalachakra #: model_terms:ir.ui.view,arch_db:kalachakra.thankyou msgid "Thank you for your generosity !" -msgstr "Merci pour votre générosité!" +msgstr "" #. module: kalachakra #: model_terms:ir.ui.view,arch_db:kalachakra.participation_form msgid "Title *" -msgstr "Titre *" +msgstr "" #. module: kalachakra #: model_terms:ir.ui.view,arch_db:kalachakra.event_calendartui msgid "Today" -msgstr "Ajourd'hui" +msgstr "" #. module: kalachakra #: model_terms:ir.ui.view,arch_db:kalachakra.kalachakra_template_donation_tax_receipt @@ -370,7 +429,7 @@ msgstr "" #. module: kalachakra #: model_terms:ir.ui.view,arch_db:kalachakra.participation_form msgid "Zip" -msgstr "Code postal" +msgstr "" #. module: kalachakra #: model:ir.model.fields,field_description:kalachakra.field_kalachakra_partnerdraftfile__active @@ -380,7 +439,7 @@ msgstr "" #. module: kalachakra #: model_terms:ir.ui.view,arch_db:kalachakra.thankyou msgid "back Home" -msgstr "Page d'accueil" +msgstr "" #. module: kalachakra #: model:ir.model.fields,field_description:kalachakra.field_event_event__calendar_id @@ -392,11 +451,26 @@ msgstr "" msgid "coucou" msgstr "" +#. module: kalachakra +#: model:ir.model.fields.selection,name:kalachakra.selection__kalachakra_membership__state__done +msgid "done" +msgstr "payé" + +#. module: kalachakra +#: model:ir.model.fields.selection,name:kalachakra.selection__kalachakra_membership__state__draft +msgid "draft" +msgstr "brouillon" + #. module: kalachakra #: model:ir.model,name:kalachakra.model_kalachakra_partnerdraftfile msgid "draft partner file" msgstr "" +#. module: kalachakra +#: model:ir.model.fields,field_description:kalachakra.field_kalachakra_membership__end_date +msgid "end date" +msgstr "date de fin" + #. module: kalachakra #: model:ir.model.fields,field_description:kalachakra.field_event_event__event_id msgid "event id" @@ -422,20 +496,58 @@ msgstr "" msgid "import draft partner" msgstr "" +#. module: kalachakra +#: model:ir.model,name:kalachakra.model_kalachakra_membership +msgid "manage membership" +msgstr "gérer les adhésions" + +#. module: kalachakra +#: model:ir.model.fields,field_description:kalachakra.field_kalachakra_membership__product_id +#: model:ir.model.fields,field_description:kalachakra.field_product_product__membership_product +#: model:ir.model.fields,field_description:kalachakra.field_product_template__membership_product +msgid "membership product" +msgstr "produit d'adhésion" + +#. module: kalachakra +#: model_terms:ir.ui.view,arch_db:kalachakra.view_membership_search +msgid "memberships of the current year" +msgstr "adhésions de l'année civile en cours" + #. module: kalachakra #: model_terms:ir.ui.view,arch_db:kalachakra.event_calendartui msgid "next" -msgstr "suite" +msgstr "" #. module: kalachakra #: model:ir.model.fields,field_description:kalachakra.field_kalachakra_partnerdraftfile_mapping__odoo_field msgid "odoo field" msgstr "" +#. module: kalachakra +#: model:ir.model.fields,field_description:kalachakra.field_kalachakra_membership__partner_id +msgid "partner" +msgstr "Contact" + #. module: kalachakra #: model_terms:ir.ui.view,arch_db:kalachakra.event_calendartui msgid "prev" -msgstr "précédent" +msgstr "" + +#. module: kalachakra +#: model:ir.model.fields,field_description:kalachakra.field_kalachakra_membership__start_date +msgid "start date" +msgstr "Date début" + +#. module: kalachakra +#: model:ir.model.fields,field_description:kalachakra.field_kalachakra_membership__state +msgid "state" +msgstr "état" + +#. module: kalachakra +#: model:ir.model.fields,field_description:kalachakra.field_product_product__super_membership_product +#: model:ir.model.fields,field_description:kalachakra.field_product_template__super_membership_product +msgid "super membership product" +msgstr "produit membre de soutien" #. module: kalachakra #: model_terms:ir.ui.view,arch_db:kalachakra.kalachakra_template_donation_tax_receipt @@ -454,3 +566,8 @@ msgstr "" #: model_terms:ir.ui.view,arch_db:kalachakra.kalachakra_template_donation_tax_receipt msgid "{{SIGNATORY}} / {{SIGNATORYJOB}}" msgstr "" + +#. module: kalachakra +#: model_terms:ir.ui.view,arch_db:kalachakra.view_membership_search +msgid "valid memberships" +msgstr "adhésions valides" \ No newline at end of file diff --git a/models/__init__.py b/models/__init__.py index f9a8345..a85bca6 100644 --- a/models/__init__.py +++ b/models/__init__.py @@ -4,4 +4,7 @@ from . import models from . import partnerimport from . import donation from . import event -#from . import partner \ No newline at end of file +from . import product +from . import membership +from . import payment_transaction +from . import partner \ No newline at end of file diff --git a/models/donation.py b/models/donation.py index c14102b..313452a 100644 --- a/models/donation.py +++ b/models/donation.py @@ -31,30 +31,7 @@ class PaymentTransaction(models.Model): class DonationDonation(models.Model): _inherit = 'donation.donation' - # payment_transaction_id = fields.Many2one('payment.transaction', string='Payment Transaction', readonly=True) - # payment_token_id = fields.Many2one( - # 'payment.token', string="Saved payment token", - # domain="""[ - # (payment_method_code == 'electronic', '=', 1), - # ('company_id', '=', company_id), - # ('acquirer_id.capture_manually', '=', False), - # ('acquirer_id.journal_id', '=', journal_id), - # ('partner_id', 'in', related_partner_ids), - # ]""", - # help="Note that tokens from acquirers set to only authorize transactions (instead of capturing the amount) are not available.") - # def _prepare_payment_transaction_vals(self): - # self.ensure_one() - # return { - # 'amount': self.amount, - # 'reference': self.ref, - # 'currency_id': self.currency_id.id, - # 'partner_id': self.partner_id.id, - # 'partner_country_id': self.partner_id.country_id.id, - # 'payment_token_id': self.payment_token_id.id, - # 'acquirer_id': self.payment_token_id.acquirer_id.id, - # 'payment_id': self.id, - # 'type': 'server2server', - # } + def _get_payment_type(self, tokenize=False): self.ensure_one() diff --git a/models/event.py b/models/event.py index 65b74d6..8baee90 100644 --- a/models/event.py +++ b/models/event.py @@ -12,7 +12,7 @@ import os.path from google.oauth2 import service_account from googleapiclient.discovery import build from datetime import datetime, timedelta - +import json # IF YOU MODIFY THE SCOPE DELETE THE TOKEN.TXT FILE SCOPES = ['https://www.googleapis.com/auth/calendar'] @@ -31,9 +31,11 @@ class EventEvent(models.Model): _description = 'Event' calendar_id=fields.Char('calendar id') - event_id=fields.Char('event id') + calendar_event_id=fields.Char('event id') def add_event_to_google_agenda(self): + + credentials = service_account.Credentials.from_service_account_file(SERVICE_ACCOUNT_FILE, scopes=SCOPES) delegated_credentials = credentials.with_subject(SUBJECT) service = build('calendar', 'v3', credentials=delegated_credentials) @@ -47,7 +49,7 @@ class EventEvent(models.Model): end=self.date_end.isoformat() diff_date=self.date_end-self.date_begin if diff_date.days>0: - date_param=self.date_begin.strftime('%Y-%m-%d')+'-'+self.date_end.strftime('%Y-%m-%d') + date_param=self.date_begin.strftime('%Y-%m-%d')+'-'+self.date_end.strftime('%Y-%m-%d')+'-'+str(self.id) else: date_param=self.date_begin.strftime('%Y-%m-%d')+'-'+str(self.id) @@ -58,8 +60,8 @@ class EventEvent(models.Model): body={"summary": self.name, "description": description, - "start": {"dateTime": start, "timeZone": 'Asia/Karachi'}, - "end": {"dateTime": end, "timeZone": 'Asia/Karachi'}, + "start": {"dateTime": start, "timeZone": 'Europe/Paris'}, + "end": {"dateTime": end, "timeZone": 'Europe/Paris'}, } #recherche de l'id calendar lié à l'étiquette de l'événément @@ -67,7 +69,8 @@ class EventEvent(models.Model): if calendar_id: event = service.events().insert(calendarId=calendar_id, body=body).execute() - self.event_id=event['id'] + self.calendar_event_id=event['id'] + self.calendar_id=calendar_id else: raise Warning('no calendar id, please check configuration tags') @@ -76,13 +79,14 @@ class EventEvent(models.Model): delegated_credentials = credentials.with_subject(SUBJECT) service = build('calendar', 'v3', credentials=delegated_credentials) - - #recherche de l'id calendar lié à l'étiquette de l'événément - calendar_id=self.tag_ids.category_id.calendar_id - if calendar_id: - service.events().delete(calendarId=calendar_id, eventId=self.event_id).execute() - - self.event_id=False - else: - raise Warning('no calendar id, please check configuration tags') + + event = service.events().get(calendarId=self.calendar_id, eventId=self.calendar_event_id).execute() + #raise Warning(json.dumps(event, indent = 4) ) + if event['status'] != "cancelled" : + service.events().delete(calendarId=self.calendar_id, eventId=self.calendar_event_id).execute() + + self.calendar_event_id=False + self.calendar_id=False + + \ No newline at end of file diff --git a/models/membership.py b/models/membership.py new file mode 100644 index 0000000..6b549a4 --- /dev/null +++ b/models/membership.py @@ -0,0 +1,140 @@ +from odoo import models, fields, api +from dateutil.relativedelta import relativedelta +from datetime import datetime + +class kalachakra_membership(models.Model): + _name = 'kalachakra.membership' + _description = 'manage membership' + + + partner_id = fields.Many2one( + 'res.partner', + string='partner', + required=True, + index=True, + track_visibility='onchange', + ondelete='restrict' + ) + product_id=fields.Many2one('product.product',required=True,string='membership product',domain="[('membership_product','=',True)]") + start_date=fields.Date('start date',required=True,default=lambda self: fields.Date.today()) + + def _default_end_date(self): + + return datetime.now()+relativedelta(years=1) + + end_date=fields.Date('end date',required=True,default=_default_end_date) + state=fields.Selection(string='state',selection=[('draft', 'draft'), ('done', 'done')],default='draft') + currency_id = fields.Many2one( + "res.currency", + string="Currency", + required=True, + tracking=True, + ondelete="restrict", + default=lambda self: self.env.company.currency_id, + ) + + + amount = fields.Monetary( + string="Amount", + currency_field="currency_id", + readonly=True, + tracking=True, + + ) + + @api.onchange('product_id') + def onchange_product_id(self): + if self.product_id: + self.amount = self.product_id.list_price + + def _get_payment_type(self, tokenize=False): + self.ensure_one() + return 'form_save' if tokenize else 'form' + + def _create_payment_transaction(self, vals): + '''Similar to self.env['payment.transaction'].create(vals) but the values are filled with the + current membership fields (e.g. the partner or the currency). + :param vals: The values to create a new payment.transaction. + :return: The newly created payment.transaction record. + ''' + # Ensure the currencies are the same. + currency = self[0].currency_id + # if any(so.pricelist_id.currency_id != currency for so in self): + # raise ValidationError(_('A transaction can\'t be linked to sales orders having different currencies.')) + + # Ensure the partner are the same. + partner = self[0].partner_id + if any(so.partner_id != partner for so in self): + raise ValidationError(_('A transaction can\'t be linked to sales orders having different partners.')) + + # Try to retrieve the acquirer. However, fallback to the token's acquirer. + acquirer_id = vals.get('acquirer_id') + acquirer = False + payment_token_id = vals.get('payment_token_id') + + if payment_token_id: + payment_token = self.env['payment.token'].sudo().browse(payment_token_id) + + # Check payment_token/acquirer matching or take the acquirer from token + if acquirer_id: + acquirer = self.env['payment.acquirer'].browse(acquirer_id) + if payment_token and payment_token.acquirer_id != acquirer: + raise ValidationError(_('Invalid token found! Token acquirer %s != %s') % ( + payment_token.acquirer_id.name, acquirer.name)) + if payment_token and payment_token.partner_id != partner: + raise ValidationError(_('Invalid token found! Token partner %s != %s') % ( + payment_token.partner.name, partner.name)) + else: + acquirer = payment_token.acquirer_id + + # Check an acquirer is there. + if not acquirer_id and not acquirer: + raise ValidationError(_('A payment acquirer is required to create a transaction.')) + + if not acquirer: + acquirer = self.env['payment.acquirer'].browse(acquirer_id) + + # Check a journal is set on acquirer. + if not acquirer.journal_id: + raise ValidationError(_('A journal must be specified for the acquirer %s.', acquirer.name)) + + if not acquirer_id and acquirer: + vals['acquirer_id'] = acquirer.id + + vals.update({ + 'amount': sum(self.mapped('amount')), + 'currency_id': currency.id, + 'partner_id': partner.id, + 'membership_ids': [(6, 0, self.ids)], + 'type': self[0]._get_payment_type(vals.get('type')=='form_save'), + }) + + transaction = self.env['payment.transaction'].create(vals) + + # Process directly if payment_token + if transaction.payment_token_id: + transaction.s2s_do_transaction() + + return transaction + + # @api.model + # def create(self,vals): + + # res=super(kalachakra_membership, self).create(vals) + + # #on ajoute l'étiquette membre au contact + # #si l'étiquette membre n'existe pas on l'a créé ! + # label_member=self.env['res.partner.category'].search([('name','=','member')]) + # id_label_member=label_member.id + # if not label_member: + # vals={} + # vals['name']='member' + # res=self.env['res.partner.category'].create(vals) + # id_label_member=res.id + # partner=self.env['res.partner'].search([('id','=',int(self.partner_id))]) + # if partner: + # if id_label_member not in partner.category_id: + # partner.write({category_id:(4, id_label_member)}) + + + diff --git a/models/partner.py b/models/partner.py index f07b1b4..3d1ebd6 100644 --- a/models/partner.py +++ b/models/partner.py @@ -1,7 +1,7 @@ from odoo import models, fields, api from odoo.exceptions import UserError, ValidationError, Warning from psycopg2 import sql, DatabaseError - +from datetime import datetime from werkzeug import utils @@ -58,5 +58,25 @@ class partner(models.Model): ('refuge','Refuge'), ('zoom','zoom'), ],'Origine', index=True) + + date_adhesion=fields.Char(string='Date adhesion', readonly=True) + + def _compute_member_status(self): + for rec in self: + member=rec.env['kalachakra.membership'].search([('partner_id','=',rec.id),('end_date','<=',datetime.now())]) + if member : rec.member_status='member' + else :rec.member_status='not member' + if rec.super_member: rec.member_status='super member' + + member_status=fields.Selection(string='member status',selection=[('not member','Not member'),('member','Member'),('super member','Super member')] + ,compute="_compute_member_status", sotre=True) + + super_member=fields.Boolean("Super member") + + @api.onchange('super_member') + def onchange_super_member(self): + + self._compute_member_status() + - date_adhesion=fields.Char(string='Date adhesion', readonly=True) \ No newline at end of file + \ No newline at end of file diff --git a/models/payment_transaction.py b/models/payment_transaction.py new file mode 100644 index 0000000..71a0423 --- /dev/null +++ b/models/payment_transaction.py @@ -0,0 +1,48 @@ +from odoo import models, fields, api,_ +from odoo.exceptions import UserError, ValidationError,Warning +from psycopg2 import sql, DatabaseError +from datetime import datetime + +from werkzeug import utils +import base64 + +class PaymentTransaction(models.Model): + _inherit = 'payment.transaction' + + donation_ids = fields.Many2many('donation.donation', 'donation_transaction_rel', 'transaction_id', 'donation_id', + string='Donations', copy=False, readonly=True) + + membership_ids = fields.Many2many('kalachakra.membership', 'membership_transaction_rel', 'transaction_id', 'membership_id', + string='Membership', copy=False, readonly=True) + + + def render_donation_button(self, donation, submit_txt=None, render_values=None): + values = { + 'partner_id': donation.partner_id.id, + 'type': self.type, + } + if render_values: + values.update(render_values) + # Not very elegant to do that here but no choice regarding the design. + self._log_payment_transaction_sent() + return self.acquirer_id.with_context(submit_class='btn btn-primary', submit_txt=submit_txt or _('Pay Now')).sudo().render( + self.reference, + donation.amount_total, + donation.currency_id.id, + values=values, + ) + def render_membership_button(self, membership, submit_txt=None, render_values=None): + values = { + 'partner_id': membership.partner_id.id, + 'type': self.type, + } + if render_values: + values.update(render_values) + # Not very elegant to do that here but no choice regarding the design. + self._log_payment_transaction_sent() + return self.acquirer_id.with_context(submit_class='btn btn-primary', submit_txt=submit_txt or _('Pay Now')).sudo().render( + self.reference, + membership.amount, + membership.currency_id.id, + values=values, + ) \ No newline at end of file diff --git a/models/product.py b/models/product.py new file mode 100644 index 0000000..e537831 --- /dev/null +++ b/models/product.py @@ -0,0 +1,13 @@ +from odoo import models, fields, api +from odoo.exceptions import UserError, ValidationError +from psycopg2 import sql, DatabaseError + +from werkzeug import utils + + +class ProductTemplate(models.Model): + _inherit = "product.template" + + membership_product = fields.Boolean(string="membership product", tracking=True) + super_membership_product = fields.Boolean(string="super membership product", tracking=True) + \ No newline at end of file diff --git a/security/ir.model.access.csv b/security/ir.model.access.csv index 97dd8b9..277b982 100644 --- a/security/ir.model.access.csv +++ b/security/ir.model.access.csv @@ -1 +1,4 @@ id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink + +access_kalachakra_membership,kalachakra.membership,model_kalachakra_membership,base.group_user,1,1,1,1 + diff --git a/static/description/membership.png b/static/description/membership.png new file mode 100644 index 0000000000000000000000000000000000000000..973df201a399285280ad1f715dafe05fa6bdc376 GIT binary patch literal 26294 zcmXtf1yEaE*EJM(ceesX3#GWb6f1=g+}+*XwKzqKJ1J0HgS!MT?(VLCp7)!7CYj9K z$>g4W&faUSz3xtwshWXe#iwc1p?Tekg8wf)My03kM@@f_@c-1R+&P;N%Ev zn|;^q3ZL^ttV?bCEy=5J2NC-1Ex9-bhsPjsEhD3t>HZfUGC7%%p7Xg@}Znn6>CJWE!*PKo#mT8jNcmQj8Z0kw|9Wf(P&z(90^hxZ-jL9X{R zS-i-OwKZQM;&Dj&o9lQm0}IFD>8Zf91Usgb84w2D$jK(bWvw`kLL3l`VzX{c7Y2`$ z#L{50hI9Nyj~njOS0p2~>Of-Sfqd=A@WYT5WR^dYrf|mu7)Nuyp{Z5Pm*8V5fYYr; zX#c0NM4yKq2Y9y%~!|wv6fpd;OL5*H)q`y~jZ9Dl^bT z^G&!}B@xHltE0ifr;|O~)m>3!>6iEqx}H&#L9Rg(Ff@UfcmE!rrYN!MLCJcxqz!vC ztXR~`oS|V&01KL`zTJfVYQrFma#*C`G}@}2AM>WNx?-iP8f7qAx7=9Zj!=v6v6{`? z@}+Od^{WAwX{D!4(thc87#8~8t0U4eX)C<7wLU|-zs=2$K`zFmA1%y{tV=L^*6l(< z+wl3W6yA8Y60crD@#EARI&du_N51%m_NaN!lBqFdXExRiu+-kjS7je330W7(y(zKq^K~BTOd<@`sC8&>lF+^7$l!<;+eQD=Os6wE>bhzLs zJ}o?oN)(gduKIJC-}$$#60xHvU{#N^s|%^sn%Q5ey60ea>}<0$*R7QD?~~DJk)BrV z{ma%b(N*4=g}*XkjPK&LnD= zp9RvI0&Uy+Ip*{f`e4XXw{fC{ouMQ?_J>Dh9x%5j(tcr+3laRH82)6qDaKJndN{z^X9o}=&&G4k9_lU8pYGrl_OSJ38G3@4_woGrvSYkrf z6xl7XNNSEVMoI-hS-!DT{hF;tgW-&;ret0`_Yd|De7j%$TH-h((FgbDheeC)JgrucbM|k$soOinP9)VU+g>9A9N&Y5y^u?iiL8ZZ;vOT>y;6gl&^wZVyA|+y2Bytzw<0kJi_u zQN)T{3MV66I1+8vY?I^$tmQW=C!WQCHyCn|A!mo3v9tB+ah~@^j?q%y!y%Qw$jXAB z@5Ou2A7GbBg6e!qL*m+nHDI1L0>Q}h-djpVoCL{4Ia?|sOza15AlF3-kmCi-7lSG0 zporzyv?<9J80?_^EQg^%nBtP9IU#1G%h;Jx2${NrH!e#hQB8lu+%v#Q`u8~=S2AB| zs30=NCS5mN!Fq}!T!9E@G5gap+@CORy~A!pgm9K-p|8Tv$B^8*z2;>k_71b#kyJEZ zc#Mbc_zsZ&bB29zIW_t2ht|PR8*xw`JvIP&_ZpdKRX}UT_>q>qdYN`76`g~8ChK#Q zab%24tbb$-?+tmBjfAJ5brBW{xs#dm2c_}Av_Mr7Ow9sxbh-x9DMX zfOxiOLSmot-RK6D4~)w!o_YUWwTCcwnI$$BGNXkY>W%I4>F!xIbeDLT9t=irY4@sM z!;=sp-8yCo#47EVg~bUOT6Te3B25c)4pM&)RLg}Ru72gDPnrq_HxF;4cl4RXEE73! z{@RYgV1<+C5hkAb*@6yOWcT!;m$NZ$^p`os(|Ui?aVzV1nwNE_MIQ73dv~+A2A8Mh zt@#u>18M6-f_Ch&A_0Oo)q)dz%!0qm?uIpu)teROCurzw2uf3dFlIE3MJJPeAg=^<^qdZAe=xs@US_O$>pdlNkFWJ{y$_vhdH^{G>&${%aEMmb-_940)WVv=TEXngo9E`Kaz@d`XeB7X zQ%CPh@&=-NwL~=WgX@XvYmhI!Nw>Zqa}DHNs!6T8)9`Z_1TDHNo;2@W@qv>*((+*A zbuO)j-T3OdQ%Q*<)AY+}$=>*SiuxOvPk+hF;61RH+FXk%MUrGalj2@ELj+-jEn}XR zLr_J|7w6FK=S3cp*XQ1wt&*A$K@=K_N7GL^X^uz|m3(YAG)@>y6h^~*A4!U6 ze}*-hMavLp#}xV#bWV{VhG!yY=1}X(m?QkOor|gnY{&~yPfUDc&s#r2g8QScyI!{` zQr5AxMfO2+iA6Z|#eHS#hX7kULYR1)Yes#fa%+K_7=fGLryR|Og~_)T3}7*23teQb zqGC`rWSQmd8qtP`yR(%2kUH2*<_0KKe&nn{zeJs=co>u z>pO$z&uwRV8roI0K50=aEI#=UvcSaAuyd9i&Dek{;dXD8{Nv~zE30V>IMgIG1EtQo zop#b$YIOea=4_bHt53xVdFVzhb`gmqJ&7T9!_XZ-0z2Qt_~b8*=$DV?euKGRlUzd> znY4n2(Dx&TESviVH@*718xS*;L7XpDc~qHTjE+_RryY$Dd!B2Xr*`r)C zW#Ye}H|l{Wc<@y3`*GL+x-2_R*2P4M7xUw&mL=+XXU|vJar%T4ua}XPGyg4^r&d`+`la(XqyQ()Q2!b^X0t&fy=bJ=EG&Jx z%*aR?`Yp3*CZR|gJ+TU3VCM0OOi=?%8QXqvStFd$A#$aH#UC78?DK$b!MBNRp9U`Fl-QD=KB78VDlAd}CJQ+y?OfgH=l?r= zEajMcJ_1x+y2AR7Z1vtWC%zvdY;It!$A-GGUgEorW>J(<0ydb zM!eP;?3x)XFX}3AwkA#}bPJV2cLiFqFt-l0xZ=kTbYoD#-;Qbar5%;!1eaFwg6}giIU4Rk@{bK)Fh`SchT=)+ z-4OYd9S8AFzQske!U{5Lc}^7tdUFf&(;ML3NxKj4G#gSj<++GXWiT0M>-9?d2fN_O zxuaSK(IG~seulu4{}8EO?4eJbCm$>Q;qw@OAplyF@?$H$WGkaS6N7ZUwdcxK7cxM)bpm8i6sjx{z$>62J#0xb<*0*Dw+ z`>-L~Cyz7)^eKn1GfMGxgK)YZ)Sk?tY^@j^Q73nL|xU$+zz^5GpwdUW?aB5B|=8t}kbJXY+3}Q>3b7{F$*(bv}p|2%$*h-q7KQE*Bj{klt4utNr+ZL>- z%e#*%lN|s!Y!Jdu>m6huVj4@mb6kQrhrtav|3wb$ruVK%v zUq|YisEwDB(KGzn!02XaQR>^tE@PpoJO~K-o)3+N3^1Q)K|w;ty6W1j<;~pmV;6G8 zd+=Bgo~o?a^so61c}W6VdP#h=tyT)G0o~*v#9c^52kX$`gq<;Al8!V#Tl#jCRu8g? zHH(?^mMeOHHGII0?B!5GSsV{6eYPf)!s`MqiJ=MkS>^Oi6G=QPc$-(e}Fe|n`((* z2IB@Zy?*W0e{2)8U+JqcgaX%4;IWKrTr}y&Hoamw;VhN^Knd}|1^9)ZWBVrZfadTM z85+X$=8`BGFH?4bBI7=)GBGksdHG>B1HMTyWMqQIG{k>C<4>VjHiv#TB&m3=XRVh- zS{tqyQCfG0ufGVuG4&o4&{Yu0$WOo^Fi=uCyeD1^srzR9yCXs(M>7(HJi$tIFr$S& z=#Y>2C({u#)n&s;m7^EW;NyG*7LEMrAd^>@|fn0CV5lLf}_YJI}O3PP7 zJxzD{82xbf2Zv}M_3)8$RnfQ@LQ5!sxCw|N!kNV0O*8J5s3f;mWe0P0)-sj$)3HV& zC>DhE&7&KAh5Kn^<`=*v&VaaDw7fh0+54p-7*cU%g;1T#m$0nKlGgjV#3@yU1yz8V zFm@j_!w@w((EaFp7fpI=kgB4nGmMb64`rHWacUf{y2NF~;u)(xI-R-z2d2+QyT5~V}Z zM<-zW@6vryilS~6^{=H=195V?W^)#^<>=_7i>F2|-j+5cO;D-RP4$&uxjo{*Gif`{ z&*J&>s)yz}2NXU5BaWMFXlFt}*?t6e?`DLJBc;+Raxt)uH{np}Kw?-N&YGnHtp&R` zg;Ij#GiG_qI1}mg)Du(;*@mc$B!ZIv@iX3gOC75ra!g~QKeA$eGK9$aBa1wA`$S7q zmyoJGaJrRSqc5NKr2uSE2guEPB?iNb4J*y6mVRUUp=m^c!-RQbWPpPemh?j#f0EB1HZHoi6}WM!)w~1gOd^` zpBhq}f#k;Bb%Ju3Z*C|gb8G_BV2xb}m+Yryb_F7f*KkSBzprkgW=`23Frj#IxP!Pv ziOD`lkaNj(pce5ggHh=VgjU-%K4lPjBEXWsrN4^W%N3}PJbK#mtRh$09?ZX`m&;<0 zmqa+1*H;(`Erm`&B7l^|0csI>`FPI86c^T(Gk-0VMMEJjl3w15-y8FB6GdMwqJ3pC z2)DI2I#C#1JzDAg=}B^Nc$f=ktTq#cXu=*lw#MUArGz)I4w=`EyUS(yQZxf4AH^t42( zmle0ayX-(hJL@^Y;KVvDfI%%;0!Wu!jgUZtn+k|VIB9gvIG>C!11;yRBy*M)pK}^f znp>1URDbQh!h~9d(y(K6UuZpsN)LlHss^0^|09(09jC)33L!$(QE00j%j#_zu?o$A zkM&7;kZ67ZGY^X``4V!?qdzFl%8Jv(364b!{k_mLBS{HGqcP`7a7E}P;uB&^Pv*Yg zXb45d=YdgI4#QB!zAc`Bp+RKtpX1DKR2L+3Vor%?Fs0}3@_Sh6)vZJPLt`vd1J#Je zL8)a0zov6Ck`wJHczSbN0UZMf+X5xM=?Ra~U}MzC;DUdlbA zrxR(NPj$U(BMKN33$LXMfoUh90#4yyP`PKjMQ`glm3zQpZ@D!8iAw5c z&U+LEQHl9dw+WedzpiXI(%(9aNRePZxKwh&BrL08S0GKq?v^Ek0CD+m6PmADiAFRr z&OjlsaRFk5q6C8rw#=~9sjXi?F(j#hES|}BJ}oa1ia@Tg2!g#^E(lg>x=E`vIPKm} zfD&POjBwh}IWsM2ng1Qn#@1=RPpm&&em1q>V@fbo)wOhb6yBWwV_w0MLz@N78f@(5 z3vpWw^eha#+Ry@VrTJFMVTk?* zdb&uZ3{4St{PEC*@VTM8h;N~nt?Zz17ZWb|EM~i4DX#w77TzRDfWi=zQclRlKK~7r z!LdRccJY4-+=Xwsc`3#4hCp^E_H0OesD?nWbqS8{2zt_}qSAJtK7dBMe8LY5HA4ho$!l4m=_%tS-qgjqT zO1chRmetJbM^hPK1xuUZ+Q00`Qt6u?;Ol9Y7z}CW>FW^__nt~uVBbSb%LwLJ>Fy2) zIBZ1#?|{<@RsQ@?!ox*#N8XBV{5m{IY-x)B$IrQMGR_4ntC0r@!}=p#zqEfQmNj_( ze<`S#+wxc79$U6?cw`Ml04lhpef8t<(#<@h!X9)#vnw+Wn#-@b81q5%KFrY~S`0)( zq6ke=?xmp5g^FBoEp$x&o+%@8y8T zUlpL8$zHZ{Wx0+^J7c?&bRwpuH_Y=-Qz*JO+ZI4UO7-9?{d~R#H2ozowNvQgFa>v2 zV5cI5{rJHO3eaD|#8fdif(mA6j~;6(svU{MpJ&gLjPlGiF=ThJ^zDRou&C)@XNF2; zIWqLPz0`n1n*>LZUp;iR1lY6^k?kI#_3k9V#t)X*L8#tocqtLV!^egx+(-+N_1=gq zmb?g+mIRr4VuwGq{%)J!ome@vYzj60xkI=8iwd?o`f6s9VP3xQ<%H&=+&#Y=lMO)r z^;E>Y(*U2x9c^+5VG&0^w|c?I*&DGj?pyc3v{SRiKjIg!eRQQsL;|it*9jAZ>G2&S z6shLb@kh6CWkpFq4S~XJSFUECF!|LaFOL%de>@4@^B+BYVq2wVghdr;`1@ab9@acj z^jq(`A7Np=4W~x03(Z}nGAnfzOe(;p zsF8JIrMuRBE;ohaCwS1XgN$YX{_Agyq54yQ*Tedvn2ccO%Bq^=D4dt-8GbR~(zo-3 z-%*x}=KD3J{pyfK#=qbEq-KUi>wC{{t3HgISxv$)U8`K>+T;QuOg&aO?h{Bx@{VpS zu0TYvf(Wx!tx`IHUw3CA#QQ0UlJB+1Z3^-u1}{LapjIcaT+EKv4x)E_7?mgv}9HVz0OP=OwN{s!` zFX`@GK-4@ud0a)+X{axdk@YZ`SM=CYFxp_CEL+kHU)=O`qwa||zd&#Qx|8hO3%=89 z;p=WBp@{=4kc7;Mq?6_lCsWy!i9V8%J)KTpdS_to$Ne)pQH0_P(aPBlXzZ(1!)Phc z8^>bL#853=T{Z-Bm(XJwI;zp|$M4ACM(+UL-7$FaRb1-Q zMYIj=j4Wp!2D^hOaif8genYqWN1a~YWR3gfenZO2QS?Vqv?LOTdYrU4KH4~DRO0dj ztFz9=6LY+4t5feCf6wRo`0#y7H{=m*9@5K)G!+{9Xp_-ZW;Wlc!ChNv9I!cB|ob>4}&`^hNHnyB& zM<)N4AL7LU-Fx(}vD%*a_GBX=`M zPFXob&;Kr5hbXrk(&yKitr=Kl{_!q+{eEeo69!YKrN|It?`~TpGL(DhbV^-46jIiz zurA6ih<5%se9FmhGb{Cn$ddv^IR@l%_wxA-p|o*m54zzGzQE7mEx>(|vOtlVn|4kK zL?Q7<_w=p1M{n`DH^C96b9Um50B?|Ats;Mw%(q6(lz;M(6({+<76I-^E4%{96&Sc* zKaH9g){Bjm>lrh&3q0TY^P(F^WvAWc-th1s<7fhD23tyvTpZoUuf4hX+5QT`1q+L5 z4nJ#g`YiKgIuHEN#wIcanKIp$5Sm8OgBoMHAog3M> z0>k)NM zxPE;)U%7+PZ8K^@tg?g;d-in^FD0O1%@KqQb<7lmA0{mcaQi*ZS9- z6IYxyX+E^H-Ggk2u*ihiLwx$rsRAo0m#d3PWE2efKL$g%Nb)iSJzRjz8?!vjFZ^se zr9UEjxgY^wk~LIxXQ%RwG3-~cX)00h;V9iCJbQlq|5<=;)CXcC5U^kEk(Sn4LjHI? zYJLHlw*B|1hw2|IJYxU-2`l;nPO^=QN!g87j#90d`P_5xD0A?iZ>J=lE1}#!l^WiR zy{cKrSN(-lE_S(azA7-Ga!5#A0LWu1z_a>IqIb$zbwBrE=5@>Rx_*y5IbgpP(6#2;XQrOS6ZnPHl5Vk~ zPWG&eT;%Q7epmoUwC&!=bvcp&)F>gliEoRct~q~wVt+U^`R;j$TwFM5vGQSey8&4F zpV+ZLR1=_c_;=L6XPWAJ-kOTGnH+GxHG{a<2~5PI-9+~hY%s!s`!4f3cOP)S9}rsE zG|Lv6O3}2p*N~mYzGRJ8l0Z8PbFwBWY+_Y-Wrf1es$3gL*PwaSpkXR)p&8qG9JzbF z@x4}*VS)I1Hxk;BxBSw+NlX}J{sh|iW;VYs*z^Khvr0;aU$2k6OD_$m>j*@YaA}TZ zr>aC(R@?`bh0Bo-U`|^}4_NEU^OennG@cr?xl;N*#W50*=gw_+vZ=!V{2Ma5tOS3T zlNErpV1MksOq0XfOrm^8+#x3@AIuYC$Y#jEjcD-R=wZogiBXKPumwBz{8>3^0U11=uN0= zSxN%L+WtS6aZXh^Mj^3STKpsqiG3X)s~Ody5U)`hF}qwG)HAY=yGK&?e8eq+SV&P2 zn$|ll5yB`9P*+CFlRd44DBuvGcG+tl=PdN>pb0Q{9}j?-6E4b<00W31r|H2d6vKhoh6L zg@_}5&{SzEEz%;FP+$YNF^o5G^_!~6CJ96SacG~9FqjXg&ZRgkA?OY~c94*IGgk2}CO+}TqR{UzaXo7R#SaM*me1U~dUlNTMxU~^*Fyq&?odDw*@g#>i-Y1mVs)E$ejsV~uv-iK? zYdLzXenw98xipgpPsrCUeE8>6M$H;sl;#K+U$9~r8evu~Wa24oWe@$~y5R%`HSZnoz_ey8&`Y7<_Gy-5*|8?|VYXs6u3$Y(rMJw>f!u zFhX8mL5^$4Rr;Oy^OUYI%H7G+J{m5QxrqZy328js;NzD^}!(S+UeAV59D%fc)1-vGz zPwghg)~IjC5n8=XwXK+UCm8Cxj$M{8rRx;QNJ!^hsB3Q(-!~o#6E;vSkKCw2rmeCy zr=N_G75|C?W%;qwQXtk^Isb_whcR{fh+ZAhe`{%QJD0oYklefs}2A8TbxA^AuO@^oOxM) z+;!d?^FO3flAhDupbU?;75du5TrmFR+sZO{P@2?(MJFmj1$v%WZz>riC`YT0)nyve7*2Xi*XZ2l?4Vq8Cq~ zJ>V{AWUqnMw3te#WNgTnS?yb>G{%$+mQ2Mm2lC_?mYF=j0)7Td!mDt{!wa*!(WMSTpYb{4tEZa_=hA?mE&y#_YSb)`v z$!{Sr8H#-*Lj~k&bRw;@OhzI9ofY`sU_ufIC%;QsaT`jB;C`!O8>T$MAjPc2PQLAi zcKj2W{{Ct3!vHOE<$+i!-*2kb2egMpEe?5M7y@cHxBKqyEi+D#C5bB*x*7jzwfh*5 z)^l>_z6Ky4P`4NtE5OaJ3rq7ZT= z#0~g2nM3_MJsx-7vDJ~c@_)SDyZ{Uz?q4okqA5U%w)NjT%GiH`OAtaV^}FE4mO_{_ z2+R&oJ0*G}dg+p`BwbI~9l{#PT5Cgc7k$E3iaXN?W}PO?HkqgVVBonfDnaCO;+cE? z>6wHoz6^T1iXMfwr8laa?IL$vcX+5X)PRpUO7&SyVvJS60>Ii6IgWqg)W9{)th`%@ zgCAWbccg%4JN`J9wM?hedve0szFWB8lXvE)$}QI+Nyiy5ZuI+KgjmG|n*2lIlYbD-4nNl8|+Fm=4RkWjVcgugasJ zu(0FeuQ|u=k=?7F;g!QA9{PkQ8g@O5{My*lN=P(z8Wz`Zp>^cwf~GIMY&*VoF@>oU zu;fak1j6M8D6vWG;0Qj5dwG2*G3b%ycnKdc!(`V(p{vY=`3nrJD8mE^ySPmf_P0zb zDR)mGonxwLU95^6Ks86M7^Q>=ZB{Lltm_mXFq2Y1sIp zoAWUDB**pKpzWYGLvIz|I!>7Ex9E2S2cg|Z(^<~`?SXOuGZ+1~!FzDb`ep6(u`wJ0 zJJSuN*Q}TvsVcYXG}`K+c2!i_`g?3CIm&+LX`FHINh?kaF76$?Xer(!CE?FxMAUoc zVy-OOeM+da#|OhCX6&vcAE2_v`!wXonOwxfKGmTtcQR$gpyR@%TqKs-PWd`c;n7Qy z_tJGJM55m!ie9@&#UnSp->gTz>6KyhC@$sn?B^jRhh`1(y_9G^Sr;MxT|@Xr`L7j2 z|FttA8a<^;T+#F!vAPqhCzD4J-LL$B4ijb(;emI zBHSR^Nv_bUm zBp#mAJ3v*Fqd$}bLHf6y3E`7h>`Jri>gk_(E|@tld6XVrhr6Jf=xgWLfHeWwnl~p6 z#%Ow*^@uncqOhQMmT`&hLP896*H&@^{D<~ zite|CWo~Cyu7N7#!|HKvT-6wZ>-_+dk?-;LMT1*J@BU;gcCZFvH_gZOI|Xdb^C91Z zvs2zg%?V4wv@_&#VtEPfV=2QgrGtYTWg(iqUgv&o=ZuB-NXW;(JZ+Rs6CbH1DMRPZ z1gf!(v{~Yq$xD9;S>a5w%g3fp^B~s%ytreKQ<2Bmx)l$@IIFcN*Z4M{Q|aI3k^N1$kG9^2o{# zNO3{uGs5($qG2z)hYi3Uqv8kdw_K|A2Nu42@OyB7geZ#lQ$%6|U!>xW)und2f>Y}N zWdQNVMj=$u=jDA;u#uCFUD91&db-#*pR`DvY6p9L!2%3lF@oh zJ(P3LOYR)xAJ*fl>8dfmK1RlTwepXw6n2BEZZnBD+s?$S-d%T=94o*)!f-Ursm8U4 zWJ!ovk|heJ9+JZX?qgH{VS5X= z{m;8vdh3N)4$VOwYVrk|uBsezys0k*jMt-7zAHWQZkN-X0cNrkSw@yh(PG9-E#57r7>m!I zuxQ&K@FLNw89#X9R_#>CQsxl$7^!8a)XESB0_=%Wb*$s^-SbRZtp8)@eQ37S?P~1h%~S&; z8<}BecwA!CZ_>M6=YS!$IhrWQXy?n_7}+_h^6Kkl`Ovf#tMSdzWWh;W_Ab?PM6e~H zpqq@TKZ%X~{#PJU7BEc#HE!fQ$5CW=B>=;UqiZ^gnBk!nA@K6M(y_Rq#SY(oWeHSg z^s3AFBa6F=>m%}SQ+$bqDx)fTBMf8sFQh>XyGUwacq1p4S{4O>E7$9DYFB9GyQ={Z zy`H+RmSx1s?}cYF$gOfz@h4cbd6*Y!`=yB6&ocT74)Kvvv-0Rf{Lvbqaxy|)wgpEA zSCVeIECjBTS0^k+`YtS-<6@`T|CBHxus5Bb{H*l#_*dx#pUSR$XJQ;GlMa{jK?5)f zG)r*raZDBB(&-Y%wWntk6)UJTN#`mZ1h3%b*36p2i%!(}SWGf(bF=|`xi=?^37|LLQwK#r;O#ep}kIoN9p zu3LAS#xm+{oVGOFbA6n-M16Dv26${GZ#d+3#@oclx3W{M-)I(T`hmhhppBFWno&Nz zC7AkxJ9?xMTM~8QrtHTC1u=jcQ3Z6Ihgolld`ikyUk!Za3ao}cGlp+Ii$~gMr+U`~ z9Sd2ReZSY#E#;Y6$0Ex0M@7GVKCUf7%#o0DiMqaW;^&tvqph+PH?H&jt>F3eE#)La z0o31`Ulaspqde#OfK8s31>rhXNHphF!ohnf%MsE|pkC0!REysllGR046o8$3=8`(A z^~L&i?w9@|+U&DUSlQW+lis4Vk#T1?DTt}>*kCJalKCo@`q%7Pxq0hJhk8+qSX{*i z;v&H>hkL7ym%HL}fT?0Ao@n@Tf9Kk}iBuCrl;g_k8qsne^X^zIIFIcDb%_KYx5$nO zcdjkh^{^NRqSq_8zz%j-NubX>`tc~R%6+%!k#}qrE>C{oaMRj zkAJerFx#1(#|oPC`_HPPtMyf0ypq-}6AI7uUSIdyNCv%452mtO4oUqW{blfIH}QQ& zrSq|2?P(;v+eZIF%XC`gG>ev1Mr;2Gv*S&uJQK<3_c>!*j5c;+JUT|&9WKMMzh_s7 zF2$;&sFI2dBNy)$V}?O-`+P(%mjPg)uTNxa`QLfOq`_khGG|}c7V>D0ip}%|b@qKL z5g5FAH?&LXd)KH}croLVxBUyrUUIKc=Sr!^_C7|WH`jj__%Pe1%wZNgf-}D|Z?(Gz z;+#O8G&GZ`SEA29;ku7TO)G^c@Uh0@bC~UT;X4nr1q3>*Udc+No7E;}#BA{`=~fP_ z8gTeRizf^&oqd+eG|eb6W(VT^Wr)vvuLTjJiV40_DbN0SnPiWS`%wM}^2JWo_9sEM zVLxaHB~P2M3Y+eZa`K&YE;x?ePj*vMhvQ@C1?@znh4v~a408uPl1_Q}z7DMScm#MM z#dDg9Q!X<^F%JfsiayuoA=qfKv8m663z5-Z`0QqiN1I+}Fg&|8ute5(>DLr)Ora4t zA0iji5Y@H;5zWz){1ccG>Y!~FQ^F^Hs~|DRUE{Fcw4CM$7xz5W|3 zpW$V2<7Slx{nbHQ^`9PfU6W5*!Z_x}7|A_bNayJ*~&<1)UImjx$(P==iJu4V{SRSUX< zvpQW#Zf>wXVUMP)9z@D1;Q;PN!VW0dnJ&S-`)<9mD$iug3<)l~7Bxj|fN#rm;(%py z69+gujbga}wUMoAx5L=N78JOqKj;OOmNhUoK%5angOT{+0I!cAelfoz(v>zJW8Am( z^YgIdGhdi_hqX+RGYQ?*zwDmNJ%0aJaEk&m=lOYpXxxnj#Y;f3MoE zEzBajLtok_MoADH4;pYWD_09bS4=T$9F}>6*62l1rHt6Ann9;Dx_RN$S;ohZ? z*0COZ`e$u~@B@Vene1;h!l4=17K3N8Wpj-WsqK*e7i0I=_+GB{F=rsdVZ(>eTZd8t zQMtbd`L_MnInh%^v3axk~R*GV}r&W*&;bS_Y1;~!i z=Ab#DgqFg#HX&)R)#gMF?-njCr$OkPJa<)WzSz&Gj6p-*Z9kWvy(57rC5s%C(zZxY z*RH6(6Q6mt7&+p5g-bS}SLBaJbFmRC9$X_Q1873*{a->vOCgN=-&Lz!V{7^xH&`>~ zyzYLH&Q1_nT?Z4~^Js^relIPiWp$z6{mj%E;?$@g(MIJzXNqs+4S^0JiSx*rk0PEmtGPFy}(+ksN^K2Bx$T%%^{5ViKXCzQ{zE7fsDEu#1P zJSC#6=%pb*C?K~?g1f%{V#*|IqfziYOy|5E$=~l;k5l05ncR9ZfzLw_QulRyz^Cl1 zv2CNZp~M3}vsL`rX9f#j827l-sJk7MD{30&-n1RaQ`Wu+-UJ^H>K?o(l@_m?=N5v* z>aP%$FYn*ehL~v6zKAZA3+?9ll+jd`{XAgGN88M3t#)QJT;9c)wlX45<2Tfm8})F0 zJ6uEBd~pb%@^{%6wlrs?J&K8k{Oo}R)UPW5T1;`!vCzAx!~#5#!D~^>#CaKkIc-4U0REw?jPx1ZxS_2 z4TT-%XzpjrMoihBoHB?6w&qN1Birb1ZSOj^H=xQEE>Q)oOt;2|sg0^U{yK zAx@?Du|2bk>=`+^v_gN%Ufd#tU^^c;EV?(u0uJS%ZD6VY#WiQU@jIV%9bM4R>Gmt8 z-*$^!L@AT0wqFAbV@n1GH$Yyi7moq#KYN!+k!4f!j>^5AwHy7pkoGNDE1<%Pgj^GM z@P{>+=GTXX-aGpcUm!}v5BKPuTQaH$UZ_9J*(y7kmWh6`_S=XUQ1=0mkTt00jPm0c z9?#9qnlR^IAB%_eokgZGWtA+APgYajh={*EOhsZMWAQLLwDU_O zjLf*H_MNFZY>_9tPKRkRV<1+4vB1{V%8Jm=8?Du(BZevk*s#?a-7dgJ6>5g*gSw0) zP|BNoZPonTgAuJr7WV=pk5LQ6BgH(4*yKDKc)Edl{3l^c_!bdq%&xLNyE`AZc5qQ@aslN*eRKLGg z=Ydzrnu-vGfdJ!fg?}{1(PkM^KK+T1@G=k5@y9(2Q51d0F&s*sA_t7ZPq3`hMVE$FyW_ zyZN2Zep&Hc@Lp{EH*|cqs{ds38B?Wm4n4$E%>#)!3HsMlc+ZEu6Oz=Iq!~B?+!MhoOK3+$l%8Rp9LWO4T1jaM?DPNnt45G zp^%&C=Igvw>bQ~KRkF=yyMu1pSL2N*Hok+4De3P469gPr$Hqx2=0UFMd_4wgUp`N& z)X}QMp9be(tx&WU_Sks0`D{EJ@DUM?lMP>w@V~}N4TzHfNx;t4s&S)dQ)9kBer(OJ zB`e3ZB1T+ovUFb@^8Q%HKRi4_r9XdfFzkJ6y$FJb@P+CFzh5t!;bxC)C{9;_=;g@A zKMo&qQrkr9-Y^QH1kbuCj_SqN=Qo9G0~p$`S|(#JJY&U{PgPK9oF03k?~K;={jk$U ztfpIC><&*de1Y^4(oVqYMVClBySgI_T7jeUp~%ntRkX}WP3OxP9G=fzp?ExeFD{!# zveGzWJ(C~9T7+}$|6fgK9oE$QHgLL;4ru`e>5>isX&l`{I^~OWNSAa`($lTz5!f(G}Cx@puoGKoBx~tjM(sCwfxjT|clG3M>Zdr<3X-f&F+(QFtpLQIHpQq-P1SlC#AlQFLO%AFS&x5mYEsS1b%cl} z$p;JG9sdm`$>z%~_A;erUyXrAKcX- z#sfAEOnN>%iCQ{NMRSTpA2;9Wv`PJGICY63*gN8gewxQt2kJa-VC|7`zv54-k%t`z zG!DfS-@`KBxZC_E9n&|0{ap#a_jXF=UCdEvYp|U03f=(>%Me1#%cNqgDy^=(U1T4- zT(W0%o~qjN!CjR>OkXa=6c@5Vrze_{jV^{j(L7W(AsY>>-Tr^CgwAnp&qxx#Jc+Qc z(Ds$>`y#bdtgsOO%K8HGhDy1sXKsZ8(5VPcaV=+YnxYjV*=pPd=;P}Y z_t?Y{NgCtqngGEHz2D8`;+oBq15>(R5|9=1`-FoXk`j}gV_9Oz=*gsBCh4*(D zI;ytHh}-!Stny^&RDUAiWzYq>K!82744zE(*g2X$qg@2k!#)O$U4GO->r>>!oeeBr zWl6Mi4~Z_h4^)|5JZFrsvG8_F{rN-(@!>TvoaM?@bidK+p1Xkm+5E@sRyYV%(X%=f zC52hnd~JL+_}bqiHPG@%3}Or;-udm+Ms&l=oNc8b<72+m_GDygxs)=u5(WO{@&ePw zA!L&|%ZWTcW%+cEn=;x+6|0l9psCwJ;6ZpI3fq-^oX1f#xs%FCH6Ynz)zK3!)FS*G z%9&%ku)A@LG$`h}p{{zAxRyDn3Yx!t#cHzpnld^|GOX?=P`M7tbq8r}CJa*5ASrUr+$Clye;t7-X(&TEyu<0jgdA6j;+=w$B-C zbe5S0D8cCp?9{_N5Z~qT=tkc54wmh!+QCX4Bh?OC7zJ&6R^s|+O`GbbLH6s=ifJunLUjE`nO~tAlX<=N}fIJqH%6c^h~&`Od-AuSls3*5J7sojB7~=q!xx z`^Wk`!O4%|>}*D{>uAlU%_|9;vLfeD`P$`)!ZS_HWfQp3sfWDE((J%i1p%@@+)H2= z-0UtOv^&N0rSnz6T4St?4m5_1@3)H9!_%9#mjBtUMw!*sBm$9qui) zorpI(T9~v_Jmpc3gl%Sns%s_rzDYP(EC8u?FjICf#s*7_6bqlNu2);#Myd~wk|;&S z`H^8%(32Xe8nhRCP2~waWzeH-_@zU%Q7^_9seTLh+06i4v2o_voh&-fCoJrQ$hjv8 zGas0;V_XKfOAbqcLRb}3HjW;Rlyl>D+y6hFK*=91U3#AP&g>$Dh*H^1>mpMY&T%B9 z1Wn0SvybBR!uC6|8H#B92ztA4x(!B%jH9kVe(uY7(-zksCsIY(YHR+21O4M*k#-%K z&Tpm;b5(dW2wPgksEKQIDU(AE6akem7G6nd);#Nf5kpZW6aU>l*d3zbj6@Mnyt{ zBBOjkY8w8rK5XsAVr7n5&L*^Rn;id4R;dGfD-y(uk7$tMUx+%Cg-k`9-L6FXw@D*X z)y0WIxz{_DMJUd4-aLHFxY1XPcVwG6?QE9QyxX1Zj4nQ6`NeDJ!Fk!;Do^>)WJtqe>*bMXWgQlj9cRrpI2b!gVmwz@bbL!i@)bI(><2t zOa+cuSe!3J#_+4Icda4DPoilWwYK))pZ$)aj|#8fn>KhT#=w%g-}GV_$9<9AyHBON zC-_NsAjs?SXzyPYa8W<~p-TOM!>R=m);B6gLNayx#z(g8xYw>-!BFH=J2C(9%O8`_ zE~v9WYrHz=(k!wt>n6m)Z$Gl>ep<)oA-|_V**0@Ir%2CmaTNm09^gc0y-3sc`xSZ( z$BObjk95DfyIpj@_-=1I5F7UwQX8sZVyAI0+0)%_XVn?H^FpGh!e5q%_>tt$0yQl( z3*tp1w>|3OB4`grIs%LUE=Y(X&ZqH8E>GHaue+SSz(!_#3E0-OZNR3ig_9MnDN)ypYHHefYpE`)5ZdVzgt6-`wOy!K-(T5u*Jr zERR)qn*?5<$i?^MmfANRn~w(zth*K8+Sqq)Ux^7jxf_aDQGads0i0GEFB8J1n8J&U zI`W~fvctPG`*IJ`wLpaB_JFQOu*+Z6p;xZMt|#Uv-5a}+cGg~DZd5Fp?3@}uyz5+k zSS}r3IR^C8bvz1!ezrl*arG=`WW7(M8oO_)LAzog2Ir`C$k0X7wZma|G&mpLI>T5W z)|8zPEBLDvc_GZ6p5i>ID+A!nIPa=Zj552RF;^7p`$vQYNk_@K&F{@TJH`dkcs!iUI#BG}Z6Jg8pFd@eW z8g+O#;0-3DPa#uNGNHtxR6&g%DCDRt%i?vDU~C2P8FRNl-mlXY@ZE@Cwa%E}V9#tL zqY2G~)6qI-@a>nqm5l0lA7&T3hk_?CjO8xOfWWsh{lNJLg-vHmIocKLpRzaImH0~6 z#xq;k^ke&1(14^{>znZ3_>6lVuTm91>aUTbj_l;>G-eDp?kpCS%((Hyiw;tQz9F02 zT#OY*_;y}1-z_^eb#W2!5Jk=1==BVi{O0B}fQDLCxm$pp%9`isxT$ zb{8!QyI)jLDj6;fJ@4|1cBsUxU=@S5j|X9SmZcjiF@9i!j`tVoGU5EAgC00raMhu3<0`KzC{$A8t=s=YWzoR+1MaYSKKHbh#bl%9E600#8@lUxj_fO7I zrtY8ZDRZAyJ7`(V32L@uMNLS^>R9yuczNKD-K30LLoN0F8Y%Z{!4!yg)hq> z(r+1fgPO+S=k4^{l)5b>y%jVEV|Npgx$Jx*JodPDmzEG?ArD6JrCPywV#9uP} zlw*^W$0ejGQmn$1vx%a19VoAKgB+;`eLZ@SZ#WK3slzST%p7ZkR~|XPC8z4b9@v47 zC6do64tTgdQG`Nq#s8(M?wyW#GjD*gw3(@jj}QaPg?b?Cvf^#Ud}LJZC)0 z2J7V^>s}*w_$3P3=WPlW{}7@L^m|c1%q#n^inVwU6$r&+PWxz3(q%?X;Ev~dsH*IL zp~vg*FI$rmzn$Twb1=<3f23=4%!%fZ7i`F<$Z4c&9!Z zddK#Cxt=7}A%(tp6dCjm(5dZZ?2o@FzG`Kh*dyVRx7%a;$*R`5nB+}Xudp&WWkQCs z=65cNJ>IX`GCgxl$D_p`;L##^EZ-0){7P3{VPgh79&ngWufyVEgmkU0WPYgHOn658`qw17W*Sz|L7q;c&*My7 z1(!F;XieH^N-$Pk74BIgFJiBisyq6i)B{XV>cvsBikMj?AeQ+hR-k~Ln8R2|b5MEH z{BN+dee3qv}%8plkE+3@Jb zn_vA$O}^s3H6%L)hNyRC1m>j?;Ia<*?)lH6v2335*%u8ac z7L3%Qpc^>R19vJ?wRnSsLo4%qAl{HsrfCA&%e~{Iqevxdep&Erz*MX zeHgLQ)j|xp!{oyF*}>p%QvC6CKbMbj{3P4^mikf{x&YU>G}7H|I@HtIy(-ZbtK z!|Oaxk_AfinjMNbIx5xV0^8PHRaR=(cXJ53K0Kxh1=tw}n2X0^Mv@{89Kp+186ykEq#lhX$t?us9_~XJW4nCczI7k0&phqhSrj1!R zJe%j3fHgzg-tOs%D_y+Cj2e5kv~$amB#tE7?(CP}<3myAxk^-yRK8M4@fi?Vp^%<_T)_q-z!J z-mzUM0t6SS90xD+n~&ZaP!)E*cr&HxK<{dRUZIuvUU$dSo;ZTUOpjNdQDt3{p5k;+ z8Z6U+N_e>XxV*@eL}n}elixksHO<3#>`sVM-0L9ct!R@5QmByj=mbeeQhje;tivm2 zYTi!Y73tn?BlBe<#&lvC-kp`c_MRSO){TG_>f?g8^p@XOpk3>O&G{M{d^86aWfBZL zwMkiQkflW4&w-)QUhj@lh?cV#(EqjObto~P+{ zvv?c3n;7gCY!@`?1sOL#gp$7XGzY@$(>2%-9;XyP8hZJ*+1B6WA2A@tTiQJ**H7VoAd z(d0xWwN^|4$z@|{e(Wc_!;apjpR2M>dH$2Xl1+1hWGEQyz}NqK+Tv}6cA-%<{)v&v zv(?fuljCbn5S!&On7-0oW^VRTe={Cy1{f_*S~0qYC$yT=hps;h30Sa+=t&;Jbjrdp zLqupHxha35My~R?ZY+Sll?y|x)*5abif9!t^UVQrQfP+zmw#B5MDKWCWh(kF$6W+# zCWe(RS^Yfs3qfRk-S|JB9&kd80nf6Wlkj%SOd=yG>qH44{Up!@Xau2}$*lT(S4cjO zM3G->e>rcw{8T5Pm?>{*Y(S&TCEB+u|2X+t9?V266}$<2S#ko4EjfTXPTA zgELudwej|FQ2L9eE3%9kJa)}YJnJ9Ga3Dj262>MoNj2XS9XLL8E z32m(3fg`B$==c*cKz<;%m2^YN1m}rxwKU*}<6VUfO$?8Sg-|Z&68QO#CD99P)2W) zsQ}X^dJx zb4GGV4f<5k`1w}vu0Adka2pN2>0j!8p3N)A^oVSwrJ+xSJ)T8>Zx$u8%WyQP~P$W1SKetZN^9-4BGHiV5)0-j(A(4+e1 zMuYR8eno{aI}Z?@;W%GGMWxiY-oAiGhPRWeFvlhvAzar}$zIHD6 zA=(8)C@QV2`J5u*+v+3#@s1^VuJ7tu2q$6=foQa%WQZ(^TtDdR+XDGSGMM8g`U~Vr z5yed30_OnX&wA@zN_HI(Y=$YOlz5o#Q@KCPhj6u)uO%?!pGF+- zB8SFI^dG-GyGYMpmE&8|3JJ`X9S05;`&e;Z&NcJ3of}cL#!Tcb#OV>T2d{5i+U@tg+3iK zdoj1s6NwL^o9^!dfGy;R4|pgn5nH%o3pERFz&{m~-A!KubRw&4j4AbsA=BgCGQwel zjW>Gjv^MqKTj~^s!zk(6IStk}AraFZGEo8_6Mzo2WMjT~-@rBj6rd`$@OZ92*CYj~ zerR)KU`s}`f6578`*P}{vTnV<=R}}oY2!8geDYTy^)hL08|!hUc9F$%eVGUUzS7t_ zvkZ3QTTv7$dQd$WjIVLF&R1{gW3C~V#?>y9gB}#69Ck`Llv^*0n_?Wp6EuMKvGW0w z69dD5$@uw;NB?IBU>Bu$sqAhV?a&lCdGB=a(t!t_plkF9wZB&wP6!>9!=uDD-RDlj z(GO_r?5Y)6W+zlKl&&pd9J&?~@Ea8bV5qmszp)~|3nWh<)emlL^&nlhl2P`Dfa?FUQYgsowW;X0Xy%dl=`iw)o zqq>nN{ZIcGQYneCV#m&DnWVk6wnlxU^V9%NP{85vVfVJKNn)jWss$E|M%Ve6GsHr8 zpVk!qM)1F(fY2twhVC>0^Pl%qe_<{333P(k);@a6~P%%zV&3!^GJ9LYi(Dnhsy zh-@2EjU2_kV-xVgmj`PTjCh!PiV0g1?RZa@e$kZbTJNdc6sa$WjnoZG{oAhoO3@pp?WX+%vZW zRc~{*YmvA%9pJA~4)_s-@JUxt;+pmNO(WTAM&Q+L9SD{4ZPkaV!V7Q$9Oe|JFajC0 zO|DxKM0jT6rE3SQ^Y{-Y8H&nVq4%&8MX^s*pdO_FJSVla#BGjQ?qVY=ak=P_M+GK9 z;)jlT-EPSs-b{h$0iIixi@qr8S<4K}z7VODUx1F@?!eV^VE;+-BLm81v4%Nq(qMr6 zRB3q?-8!*Y(D|#qTp<-Odlc2(5*y@%lE zmQ1pc@x>0{COmcLIbw8Ra^2d6H)wxy+%)28S892zeD+GXPj8~UiCH?&u{CosN-&^g z*w?%)?BO8bpt-5g=!nG(W8sW;_k|zx#r`A%AV~^>L}Q+TjZSOs*^h^UrTN3``@)=n zlV?3zZKOePi~8x=y63`TOgk5c1{!lCLs8p(3Fo5)OMBDhdDxwLyL=1&jWP33D)~mr z_Z4_B_j5zP4UtOVx=BJz_kdT7RN%348=6hO!m?Acf{GX8w@(t5lDiVO390K&}Gnol162__k@IDp+U&i7f z$(C!1+?hLq^W>o*|AXjng1M03t3%yAdWKruxYZe(N!Tm3oX`ACn@xe9imPv;J}c@eP%pr~LOS(P}dmy&_qAiUt)) z{=o_^zM0<7YHTk}x|PtA62}F+qapoYpEO9XU|Z!wg4c_V|DqJlqZ#B+DZC#ho*i=| zV%HKNrtPq3Jyh&V+Eh<_$F>W03iCa%s&WJGtGk} z7uqKi z2^M->ntX;z)E*T<=%?}Y{|qQ?GcZLQehMtbGzJ3H;mo~fdHes8xoO`-^m|QN^KY&Q zF+O@&01UM0aAB6!GOr;EhDvn)Tq8497xe(0NH~}n+WEteJT=C2i5_XP9(cjB)XmAQZy>77H8%lY}A$ziLh{xN&@8!SW zvGV;AzV@r@DKYipTWWY(NL@BG87c5hRJXY3!_5$<_R%bs*wM!=cQC@UodBCM21;cVH`85-_u6- zHeyE!m!sq0Z7TL-O6gZ-5#G$zIY!<6N(Fv;ViETpQ*|Jv$o`Gy@z=Q@OamJec~7f8 z-QV!Fq8Q!%zp0gQW6L%^dfN#r6*`lSXmoZn!5PYA9v^QlV#Waf>_qP2~Fl(0a zy7N=D({ZB7!W|?ma<9JlXGdNQlF-RgNKU4;G3foYc=(N5uvKU|4fQgX`pq%kPyNVu$`Mf0oXT$148;a0t>ya<|@c&%%ez$e41#*{D1Cp+TWHHbtGLX=0J4B6vu zcYn{15T<1+|4qawR;Ep2Q$Y+s`>nC6_*4Q(pS{KIS+Z)V+)8@aaqC0!ykay#b(`== zA?&-nurcvf?QDBciM*VKtdlmswv^ym?yHN{cdjs%vM6W0NzVG5p$t(hrc??>%}K)oZA3 zrOV8v@7*<=lx|IPu(P1IcX)4SQXA_1?%mR#zgsovuqQ5&f62nQH#a=cNrf1|M?(el Ks!qu|^8Wx53SBt> literal 0 HcmV?d00001 diff --git a/views/event.xml b/views/event.xml index 500854f..4f8d370 100644 --- a/views/event.xml +++ b/views/event.xml @@ -21,8 +21,9 @@ -