from django.db import models from outils.common_imports import * from outils.common import OdooAPI from outils.common import CouchDB import datetime class CagetteEnvelops(models.Model): """Class to manage operations on envelops""" def __init__(self): """Init with odoo id.""" self.o_api = OdooAPI() self.c_db = CouchDB(arg_db='envelops') def get_all(self): envelops = [] alldocs = self.c_db.getAllDocs() if len(alldocs) > 0: for e in alldocs: if 'type' in e: envelops.append(e) return envelops def get_ids_in_all(self): ids = [] envelops = self.get_all() if len(envelops) > 0: for e in envelops: for key, val in e['envelop_content'].items(): if not (key in ids): ids.append(key) return ids def get_related_invoice(self, data): invoice = None message = "" # Get specific invoice if id is given if 'invoice_id' in data: cond = [['id', '=', data['invoice_id']], ["number", "!=", False]] else: cond = [['partner_id', '=', data['partner_id']], ["number", "!=", False], ["residual_signed", ">", 0]] fields = ['id', 'name', 'number', 'partner_id', 'residual_signed'] invoice_res = self.o_api.search_read('account.invoice', cond, fields,order="residual_signed DESC") # Check if invoice exists if len(invoice_res) > 0: # Get first invoice for which amount being paid <= amount left to pay in invoice for invoice_item in invoice_res: if int(float(data['amount']) * 100) <= int(float(invoice_item['residual_signed']) * 100): invoice = invoice_item if invoice is None: message = 'The amount is too high for the invoices found for this partner.' if invoice is None and 'invoice_id' in data: """Because of a legacy bug, some capital subscription recording processes could save a wrong 0 amount invoice in envelop, instead of the one with right amount. So, let's retry without invoice_id""" del data['invoice_id'] [invoice, message] = self.get_related_invoice(data) # Can't fall into a loop since invoice_id key has been deleted return [invoice, message] def save_payment(self, data): """Save a partner payment""" res = { "done": False } try: # Get invoice [invoice, message] = self.get_related_invoice(data) if invoice is None: if len(message) == 0: message = 'No invoice found for this partner, can\'t create payment.' res['error'] = message return res payment_journal_id = None for pm in settings.SUBSCRIPTION_PAYMENT_MEANINGS: if pm['code'] == data['type']: payment_journal_id = pm['journal_id'] args = { "writeoff_account_id": False, "payment_difference_handling": "open", "payment_date": datetime.date.today().strftime("%Y-%m-%d"), "currency_id": 1, "amount": data['amount'], "payment_method_id": 1, "journal_id": payment_journal_id, "partner_id": data['partner_id'], "partner_type": "customer", "payment_type": "inbound", "communication": invoice['number'], "invoice_ids": [(4, invoice['id'])] } try: payment_id = self.o_api.create('account.payment', args) except Exception as e: res['error'] = repr(e) coop_logger.error(res['error'] + ": %s", str(args)) # Exception rises when odoo method returns nothing marshal_none_error = 'cannot marshal None unless allow_none is enabled' try: # Second operation to complete payment registration self.o_api.execute('account.payment', 'post', [payment_id]) except Exception as e: if not (marshal_none_error in str(e)): res['error'] = repr(e) coop_logger.error(res['error'] + ' : %s', str(payment_id)) if not ('error' in res): try: if int(float(data['amount']) * 100) == int(float(invoice['residual_signed']) * 100): # This payment is what was left to pay, so change invoice state self.o_api.update('account.invoice', [invoice['id']], {'state': 'paid'}) except Exception as e: res['error'] = repr(e) coop_logger.error(res['error']) except Exception as e: res['error'] = repr(e) coop_logger.error(res['error'] + ' : %s', str(data)) if not ('error' in res): res['done'] = True res['payment_id'] = payment_id return res def delete_envelop(self, envelop): return self.c_db.delete(envelop) def archive_envelop(self, envelop): envelop['archive'] = True envelop['cashing_date'] = datetime.date.today().strftime("%d/%m/%Y") return self.c_db.dbc.update([envelop]) def generate_envelop_display_id(self): """Generate a unique incremental id to display""" c_db = CouchDB(arg_db='envelops') display_id = 0 # Get last created envelop: the one with the highest display_id envelops = c_db.getAllDocs(descending=True) if envelops: last_env = envelops[0] if 'display_id' in last_env: try: if int(last_env['display_id']) > display_id: display_id = int(last_env['display_id']) except: pass display_id += 1 return display_id @staticmethod def create_or_update_envelops(payment_data): """Create or update one or multiple envelops according to member payment data""" c_db = CouchDB(arg_db='envelops') m = CagetteEnvelops() answer = None doc = { 'type' : payment_data['payment_meaning'], 'creation_date' : datetime.date.today().strftime("%Y-%m-%d"), 'envelop_content': { payment_data['partner_id'] : { 'partner_name' : payment_data['partner_name'] } } } # Create or update today's envelop when payment is cash if payment_data['payment_meaning'] == 'cash': # Generate envelop id today = datetime.date.today() envelop_id = 'cash_' + str(today.year) + '_' + str(today.month) + '_' + str(today.day) try: doc = c_db.getDocById(envelop_id) #today's envelop already exists doc['envelop_content'][payment_data['partner_id']] = { 'partner_name': payment_data['partner_name'], 'amount': int(payment_data['shares_euros']) } answer = c_db.dbc.update([doc]) except: #doesn't exist, create today's envelop doc['_id'] = envelop_id doc['envelop_content'][payment_data['partner_id']]['amount'] = int(payment_data['shares_euros']) doc.pop('_rev', None) answer = c_db.dbc.save(doc) # When payment by check else: # Get the oldest check envelops, limited by the number of checks docs = [] try: for item in c_db.dbc.view('index/by_type_not_archive', key='ch', include_docs=True, limit=payment_data['checks_nb']): docs.append(item.doc) except: # Exception raised if no envelop pass # If only 1 check to save if int(payment_data['checks_nb']) == 1: # No existing envelop, create one if len(docs) == 0: doc['_id'] = 'ch_' + str(datetime.datetime.now().timestamp()) doc['display_id'] = m.generate_envelop_display_id() doc['envelop_content'][payment_data['partner_id']]['amount'] = int(payment_data['shares_euros']) doc.pop('_rev', None) answer = c_db.dbc.save(doc) # Update existing envelop else: docs[0]['envelop_content'][payment_data['partner_id']] = doc['envelop_content'][payment_data['partner_id']] docs[0]['envelop_content'][payment_data['partner_id']]['amount'] = int(payment_data['shares_euros']) answer = c_db.dbc.update(docs) # If more than 1 check else: checks_cpt = 0 # Put the first checks in the first existing envelops for item in docs: item['envelop_content'][payment_data['partner_id']] = doc['envelop_content'][payment_data['partner_id']] item['envelop_content'][payment_data['partner_id']]['amount'] = payment_data['checks'][checks_cpt] checks_cpt += 1 answer = c_db.dbc.update([item]) # If there is no existing envelop for the reminding checks, create them env_display_id = m.generate_envelop_display_id() for i in range(checks_cpt, int(payment_data['checks_nb'])): # rangeMAX excluded -> no loop if no remaining checks doc['_id'] = 'ch_' + str(datetime.datetime.now().timestamp() + i) doc['display_id'] = env_display_id doc['envelop_content'][payment_data['partner_id']]['amount'] = payment_data['checks'][checks_cpt] doc.pop('_rev', None) # For some reason, the _rev of the previously created doc is stored and set to the next new doc answer = c_db.dbc.save(doc) checks_cpt += 1 env_display_id += 1 return answer