lists.arthurdejong.org
RSS feed

python-stdnum branch master updated. 1.11-16-g5d0f288

[Date Prev][Date Next] [Thread Prev][Thread Next]

python-stdnum branch master updated. 1.11-16-g5d0f288



This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "python-stdnum".

The branch, master has been updated
       via  5d0f288eb18793048fa10f9d9de82df087f7e71e (commit)
      from  7fb390e748e7125d91c2fa4723eb6f4189da68cf (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
https://arthurdejong.org/git/python-stdnum/commit/?id=5d0f288eb18793048fa10f9d9de82df087f7e71e

commit 5d0f288eb18793048fa10f9d9de82df087f7e71e
Author: Jeffry Jesus De La Rosa <jeffryjesus@gmail.com>
Date:   Mon Jun 10 16:55:05 2019 -0400

    Support Dominican Republic e-CF within NCF
    
    e-CF is the new way of DGII document, is the same as NCF, but the
    difference one to another, is that e-CF has 13 digit and is electronic
    invoice, with this change it will validate the correct NCF and e-CF.
    
    Closes https://github.com/arthurdejong/python-stdnum/pull/135

diff --git a/stdnum/do/ncf.py b/stdnum/do/ncf.py
index 5a03fb8..ce53d16 100644
--- a/stdnum/do/ncf.py
+++ b/stdnum/do/ncf.py
@@ -22,16 +22,29 @@
 
 """NCF (Números de Comprobante Fiscal, Dominican Republic receipt number).
 
-The NCF is used to number invoices and other documents for the purposes of
-tax filing. The number is 19 digits long and consists of a letter (A or P) to
-indicate that the number was assigned by the taxpayer or the DGIT, followed a
-2-digit business unit number, a 3-digit location number, a 3-digit mechanism
-identifier, a 2-digit document type and a 8-digit serial number.
+The NCF is used to number invoices and other documents for the purpose of tax
+filing. The e-CF (Comprobante Fiscal Electrónico) is used together with a
+digital certificate for the same purpose. The number is either 19, 11 or 13
+(e-CF) digits long.
+
+The 19 digit number starts wit a letter (A or P) to indicate that the number
+was assigned by the taxpayer or the DGII, followed a 2-digit business unit
+number, a 3-digit location number, a 3-digit mechanism identifier, a 2-digit
+document type and a 8-digit serial number.
+
+The 11 digit number always starts with a B followed a 2-digit document type
+and a 7-digit serial number.
+
+The 13 digit e-CF starts with an E followed a 2-digit document type and an
+8-digit serial number.
 
 More information:
 
  * https://www.dgii.gov.do/
+ * https://dgii.gov.do/legislacion/normas/Documents/Norma05-19.pdf
 
+>>> validate('E010000000005')  # format since 2019-04-08
+'E010000000005'
 >>> validate('B0100000005')  # format since 2018-05-01
 'B0100000005'
 >>> validate('A020010210100000005')  # format before 2018-05-01
@@ -53,30 +66,39 @@ def compact(number):
 
 
 # The following document types are known:
-#  01 invoices for fiscal declaration (or tax reporting)
-#  02 invoices for final consumer
-#  03 debit note
-#  04 credit note (refunds)
-#  11 informal supplier invoices (purchases)
-#  12 single income record
-#  13 minor expenses invoices (purchases)
-#  14 invoices for special customers (tourists, free zones)
-#  15 invoices for the government
+_document_types = (
+    '01',  # invoices for fiscal declaration (or tax reporting)
+    '02',  # invoices for final consumer
+    '03',  # debit note
+    '04',  # credit note (refunds)
+    '11',  # informal supplier invoices (purchases)
+    '12',  # single income record
+    '13',  # minor expenses invoices (purchases)
+    '14',  # invoices for special customers (tourists, free zones)
+    '15',  # invoices for the government
+)
+
 
 def validate(number):
     """Check if the number provided is a valid NCF."""
     number = compact(number)
-    if len(number) == 11:
+    if len(number) == 13:
+        if number[0] != 'E' or not isdigits(number[1:]):
+            raise InvalidFormat()
+        if number[1:3] not in _document_types:
+            raise InvalidComponent()
+    elif len(number) == 11:
         if number[0] != 'B' or not isdigits(number[1:]):
             raise InvalidFormat()
+        if number[1:3] not in _document_types:
+            raise InvalidComponent()
     elif len(number) == 19:
         if number[0] not in 'AP' or not isdigits(number[1:]):
             raise InvalidFormat()
+        if number[9:11] not in _document_types:
+            raise InvalidComponent()
     else:
         raise InvalidLength()
-    if number[-10:-8] not in (
-            '01', '02', '03', '04', '11', '12', '13', '14', '15'):
-        raise InvalidComponent()
     return number
 
 
diff --git a/tests/test_do_ncf.doctest b/tests/test_do_ncf.doctest
index 1c815cf..d0a6475 100644
--- a/tests/test_do_ncf.doctest
+++ b/tests/test_do_ncf.doctest
@@ -27,7 +27,7 @@ really useful as module documentation.
 
 Some basic tests for invalid numbers:
 
->>> ncf.validate('FJ10010010100000004')
+>>> ncf.validate('FJ10010010100000004')  # should start with A or P
 Traceback (most recent call last):
     ...
 InvalidFormat: ...
@@ -39,14 +39,30 @@ InvalidLength: ...
 Traceback (most recent call last):
     ...
 InvalidLength: ...
->>> ncf.validate('A010010010500000004')
+>>> ncf.validate('Z010010011600000004')  # should start with A or P
+Traceback (most recent call last):
+    ...
+InvalidFormat: ...
+>>> ncf.validate('A010010019900000004')  # document type 99 invalid
+Traceback (most recent call last):
+    ...
+InvalidComponent: ...
+>>> ncf.validate('Z0100000005')  # should start with B
+Traceback (most recent call last):
+    ...
+InvalidFormat: ...
+>>> ncf.validate('B9900000005')  # document type 99 invalid
 Traceback (most recent call last):
     ...
 InvalidComponent: ...
->>> ncf.validate('Z010010011600000004')
+>>> ncf.validate('Q010000000005')  # should start with E
 Traceback (most recent call last):
     ...
 InvalidFormat: ...
+>>> ncf.validate('E990000000005')  # document type 99 invalid
+Traceback (most recent call last):
+    ...
+InvalidComponent: ...
 
 
 These have been found online and should all be valid numbers.

-----------------------------------------------------------------------

Summary of changes:
 stdnum/do/ncf.py          | 58 ++++++++++++++++++++++++++++++++---------------
 tests/test_do_ncf.doctest | 22 +++++++++++++++---
 2 files changed, 59 insertions(+), 21 deletions(-)


hooks/post-receive
-- 
python-stdnum