10 Commits 397aaa3337 ... a9e24b4aaf

Author SHA1 Message Date
  Jocelyn Delalande a9e24b4aaf Merge remote-tracking branch 'ARN/fix-misc-stuff-in-csv-import_' 6 years ago
  ljf 571df323fe Merge branch 'fix-requirements' of FFDN/coin into master 6 years ago
  ljf 5b37a3fbe5 [fix] Fix loalize issue due to missing requirements 6 years ago
  Jocelyn Delalande aa02805c73 Merge remote-tracking branch 'ARN/enh-disable-invoice-comment_' 6 years ago
  Alexandre Aubin a6e9e7ed4d Specify unidecode version 7 years ago
  Alexandre Aubin f652fb8de9 Document INVOICES_INCLUDE_CONFIG_COMMENTS in README 7 years ago
  Alexandre Aubin f2a4464f89 Change setting name to be more explicit 7 years ago
  ljf 413b1138a6 [enh] Disable invoice comment 7 years ago
  ljf 3d36136d63 [enh] Convert iso to utf8 csv file 7 years ago
  Alexandre Aubin 1192b2c094 Misc fixes/improvements in CSV import from bank and matching 7 years ago

+ 1 - 0
README.md

@@ -343,6 +343,7 @@ List of available settings in your `settings_local.py` file.
 - `PAYMENT_DELAY`: Payment delay in days for issued invoices ( default is 30 days which is the default in french law)
 - `PAYMENT_DELAY`: Payment delay in days for issued invoices ( default is 30 days which is the default in french law)
 - `MEMBER_CAN_EDIT_PROFILE`: Allow members to edit their profiles
 - `MEMBER_CAN_EDIT_PROFILE`: Allow members to edit their profiles
 - `HANDLE_BALANCE`: Allows to handle money balances for members (False default)
 - `HANDLE_BALANCE`: Allows to handle money balances for members (False default)
+- `INVOICES_INCLUDE_CONFIG_COMMENTS`: Add comment related to a subscription configuration when generating invoices
 
 
 Accounting logs
 Accounting logs
 ---------------
 ---------------

+ 3 - 2
coin/billing/create_subscriptions_invoices.py

@@ -11,7 +11,7 @@ from django.core.exceptions import ObjectDoesNotExist
 from coin.offers.models import Offer, OfferSubscription
 from coin.offers.models import Offer, OfferSubscription
 from coin.members.models import Member
 from coin.members.models import Member
 from coin.billing.models import Invoice, InvoiceDetail
 from coin.billing.models import Invoice, InvoiceDetail
-
+from django.conf import settings
 
 
 def create_all_members_invoices_for_a_period(date=None):
 def create_all_members_invoices_for_a_period(date=None):
     """
     """
@@ -140,7 +140,8 @@ def create_member_invoice_for_a_period(member, date):
                 # à la facture
                 # à la facture
                 label = offer.name
                 label = offer.name
                 try:
                 try:
-                    if (offer_subscription.configuration.comment):
+                    if settings.INVOICES_INCLUDE_CONFIG_COMMENTS and \
+                    (offer_subscription.configuration.comment):
                         label += " (%s)" % offer_subscription.configuration.comment
                         label += " (%s)" % offer_subscription.configuration.comment
                 except ObjectDoesNotExist:
                 except ObjectDoesNotExist:
                     pass
                     pass

+ 28 - 11
coin/billing/management/commands/import_payments_from_csv.py

@@ -27,6 +27,8 @@ import logging
 import os
 import os
 import re
 import re
 
 
+import unidecode
+
 # Django specific imports
 # Django specific imports
 from argparse import RawTextHelpFormatter
 from argparse import RawTextHelpFormatter
 from django.core.management.base import BaseCommand, CommandError
 from django.core.management.base import BaseCommand, CommandError
@@ -45,7 +47,7 @@ DATE_FORMAT="%d/%m/%Y"
 ID_REGEX=r"(?i)(\b|_)ID[\s\-\_\/]*(\d+)(\b|_)"
 ID_REGEX=r"(?i)(\b|_)ID[\s\-\_\/]*(\d+)(\b|_)"
 # If the label of the payment contains one of these, the payment won't be
 # If the label of the payment contains one of these, the payment won't be
 # matched to a member when importing it.
 # matched to a member when importing it.
-KEYWORDS_TO_NOTMATCH=[ "DON", "MECENAT", "REM CHQ" ]
+KEYWORDS_TO_NOTMATCH=[ "REM CHQ" ]
 
 
 class Command(BaseCommand):
 class Command(BaseCommand):
 
 
@@ -76,9 +78,10 @@ class Command(BaseCommand):
     def handle(self, *args, **options):
     def handle(self, *args, **options):
 
 
         assert options["filename"] != ""
         assert options["filename"] != ""
-
         if not os.path.isfile(options["filename"]):
         if not os.path.isfile(options["filename"]):
             raise CommandError("This file does not exists.")
             raise CommandError("This file does not exists.")
+        os.system("iconv -f ISO-8859-1 -t UTF-8 %s > %s.utf8.csv" % (options["filename"], options["filename"]))
+        options["filename"] = options["filename"] + '.utf8.csv'
 
 
         payments = self.convert_csv_to_dicts(self.clean_csv(self.load_csv(options["filename"])))
         payments = self.convert_csv_to_dicts(self.clean_csv(self.load_csv(options["filename"])))
 
 
@@ -189,13 +192,14 @@ class Command(BaseCommand):
 
 
     def try_to_match_payment_with_members(self, payments):
     def try_to_match_payment_with_members(self, payments):
 
 
-        members = Member.objects.filter(status="member")
+        #members = Member.objects.filter(status="member")
+        members = Member.objects.all()
 
 
         idregex = re.compile(ID_REGEX)
         idregex = re.compile(ID_REGEX)
 
 
         for payment in payments:
         for payment in payments:
 
 
-            payment_label = payment["label"]
+            payment_label = payment["label"].upper()
 
 
             # First, attempt to match the member ID
             # First, attempt to match the member ID
             idmatches = idregex.findall(payment_label)
             idmatches = idregex.findall(payment_label)
@@ -211,11 +215,14 @@ class Command(BaseCommand):
             # Second, attempt to find the username
             # Second, attempt to find the username
             usernamematch = None
             usernamematch = None
             for member in members:
             for member in members:
-                matches = re.compile(r"(?i)(\b|_)"+re.escape(member.username)+r"(\b|_)") \
+                username = self.flatten(member.username)
+                matches = re.compile(r"(?i)(\b|_)"+re.escape(username)+r"(\b|_)") \
                             .findall(payment_label)
                             .findall(payment_label)
+
                 # If not found, try next
                 # If not found, try next
                 if len(matches) == 0:
                 if len(matches) == 0:
                     continue
                     continue
+
                 # If we already had a match, abort the whole search because we
                 # If we already had a match, abort the whole search because we
                 # have multiple usernames matched !
                 # have multiple usernames matched !
                 if usernamematch != None:
                 if usernamematch != None:
@@ -236,23 +243,31 @@ class Command(BaseCommand):
                 if member.last_name == "":
                 if member.last_name == "":
                     continue
                     continue
 
 
-                matches = re.compile(r"(?i)(\b|_)"+re.escape(str(member.last_name))+r"(\b|_)") \
+                # "Flatten" accents in the last name... (probably the CSV
+                # don't contain 'special' chars like accents
+                member_last_name = self.flatten(member.last_name)
+
+                matches = re.compile(r"(?i)(\b|_)"+re.escape(member_last_name)+r"(\b|_)") \
                             .findall(payment_label)
                             .findall(payment_label)
+
                 # If not found, try next
                 # If not found, try next
                 if len(matches) == 0:
                 if len(matches) == 0:
                     continue
                     continue
+
                 # If this familyname was matched several time, abort the whole search
                 # If this familyname was matched several time, abort the whole search
-                if len(matches) > 1:
-                    familynamematch = None
-                    break
+                #if len(matches) > 1:
+                #    print("Several matches ! Aborting !")
+                #    familynamematch = None
+                #    break
+
                 # If we already had a match, abort the whole search because we
                 # If we already had a match, abort the whole search because we
                 # have multiple familynames matched !
                 # have multiple familynames matched !
                 if familynamematch != None:
                 if familynamematch != None:
                     familynamematch = None
                     familynamematch = None
                     break
                     break
 
 
-                familynamematch = str(member.last_name)
-                usernamematch = str(member.username)
+                familynamematch = member_last_name
+                usernamematch = member.username
 
 
             if familynamematch != None:
             if familynamematch != None:
                 payment["member_matched"] = usernamematch
                 payment["member_matched"] = usernamematch
@@ -336,3 +351,5 @@ class Command(BaseCommand):
                                              date=new_payment["date"],
                                              date=new_payment["date"],
                                              member=member)
                                              member=member)
 
 
+    def flatten(self, some_string):
+        return unidecode.unidecode(some_string).upper()

+ 3 - 0
coin/settings_base.py

@@ -275,3 +275,6 @@ MEMBER_CAN_EDIT_PROFILE = False
 
 
 # Allows to deactivate displays and calculations of balances.
 # Allows to deactivate displays and calculations of balances.
 HANDLE_BALANCE = False
 HANDLE_BALANCE = False
+
+# Add subscription comments in invoice items
+INVOICES_INCLUDE_CONFIG_COMMENTS = True

+ 3 - 1
requirements.txt

@@ -1,5 +1,5 @@
 Django>=1.8.17,<1.9
 Django>=1.8.17,<1.9
-psycopg2==2.5.2
+psycopg2==2.5.4
 python-ldap==2.4.15
 python-ldap==2.4.15
 wsgiref==0.1.2
 wsgiref==0.1.2
 python-dateutil==2.2
 python-dateutil==2.2
@@ -16,3 +16,5 @@ feedparser
 six==1.10.0
 six==1.10.0
 WeasyPrint==0.31
 WeasyPrint==0.31
 freezegun==0.3.8
 freezegun==0.3.8
+pytz>=2018.5
+unidecode>=1.0,<1.1