Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WsxyCHJYL6W9MZ6FPnH8ax
450 lines
20 KiB
Plaintext
450 lines
20 KiB
Plaintext
* test_storno_efactura.prg
|
|
* Teste StornoEFacturaXML/GetXmlOriginalEFactura (COMUN\programe\anaf_efactura.prg) si
|
|
* rutarea CN/FCN + B2C din SendEfactura/ValidareEFacturaANAF (verificare fara retea).
|
|
* Fixturi in COMUN\utile\Teste\storno\ (anonimizate, cu diacritice reale la fixtura din id 3795).
|
|
*
|
|
* Rulare: vfp9.exe -A -T "D:\ROA\ROACONT\COMUN\utile\Teste\storno\test_storno_efactura.prg"
|
|
* Iesire in D:\ROA\ROACONT\COMUN\utile\Teste\storno\test_storno_efactura_log.txt
|
|
|
|
SET SAFETY OFF
|
|
SET TALK OFF
|
|
SET DELETED ON
|
|
SET EXACT ON
|
|
SET CENTURY ON
|
|
SET DATE DMY
|
|
SET NULLDISPLAY TO ''
|
|
CLOSE DATABASES
|
|
|
|
PUBLIC gcUILog, gcFixDir, gcOutDir
|
|
gcFixDir = "D:\ROA\ROACONT\COMUN\utile\Teste\storno\"
|
|
gcOutDir = "D:\ROA\ROACONT\COMUN\utile\Teste\storno\out\"
|
|
IF !DIRECTORY(gcOutDir)
|
|
MD (gcOutDir)
|
|
ENDIF
|
|
gcUILog = gcFixDir + "test_storno_efactura_log.txt"
|
|
STRTOFILE("START " + TTOC(DATETIME()) + CHR(13) + CHR(10), gcUILog)
|
|
|
|
ON ERROR DO PrErrLocal WITH ERROR(), MESSAGE(), PROGRAM(), LINENO()
|
|
ON SHUTDOWN QUIT
|
|
|
|
PUBLIC lnPass, lnFail
|
|
lnPass = 0
|
|
lnFail = 0
|
|
|
|
TRY
|
|
DO "D:\ROA\ROACONT\COMUN\utile\Teste\test_init_env_auto.prg" WITH 'CENTRAL', 'MARIUSM_AUTO', 'ROMFASTSOFT'
|
|
IF gnHandle <= 0
|
|
DO PrLogLocal WITH 'FAIL: conectare Oracle esuata (gnHandle=' + TRANSFORM(gnHandle) + ')'
|
|
QUIT
|
|
ENDIF
|
|
DO PrLogLocal WITH 'mediu initializat, gnHandle=' + TRANSFORM(gnHandle) + ' gnAn=' + TRANSFORM(gnAn) + ' gnLuna=' + TRANSFORM(gnLuna)
|
|
|
|
*=========================================================================
|
|
DO PrLogLocal WITH '=== TEST 0: functiile noi sunt incarcate (SET PROCEDURE anaf_efactura.prg din init) ==='
|
|
LOCAL lcMsg0, llRet0
|
|
LOCAL laMod0[1,4]
|
|
llRet0 = StornoEFacturaXML('', '', '', gcOutDir + 't0.xml', @laMod0, @lcMsg0)
|
|
DO Assert WITH !llRet0 AND 'gol' $ LOWER(lcMsg0), 'StornoEFacturaXML e apelabila si valideaza XML gol', 'retur=' + TRANSFORM(llRet0) + ' mesaj=[' + lcMsg0 + ']'
|
|
|
|
*=========================================================================
|
|
DO PrLogLocal WITH '=== TEST 1 + 1b + 2: round-trip dubla negare + diacritice + noduri neatinse (fixtura reala id 3795) ==='
|
|
DO TestRoundTrip WITH 'storno_fixture_diacritice_reale.xml', 'diacritice_reale', .T.
|
|
|
|
DO PrLogLocal WITH '=== TEST 1 + 2: round-trip (fixtura simpla cu reduceri/majorari pe document) ==='
|
|
DO TestRoundTrip WITH 'storno_fixture_diacritice_reale.xml', 'diacritice_reale_bis', .F.
|
|
|
|
DO PrLogLocal WITH '=== TEST 1 + 1c: round-trip + namespace corect pe prefixe custom n1/n2/n3 ==='
|
|
DO TestRoundTrip WITH 'storno_fixture_prefix_n3.xml', 'prefix_n3', .F.
|
|
DO TestNamespaceN3
|
|
|
|
DO PrLogLocal WITH '=== TEST 1: round-trip pe CreditNote minimal ==='
|
|
DO TestRoundTrip WITH 'storno_fixture_creditnote_minimal.xml', 'creditnote_minimal', .F.
|
|
|
|
DO PrLogLocal WITH '=== TEST 1: round-trip pe factura deja negativa (D13: permisa) ==='
|
|
DO TestRoundTrip WITH 'storno_fixture_factura_negativa.xml', 'factura_negativa', .F.
|
|
|
|
DO PrLogLocal WITH '=== TEST 1: sume zero / -0.00 raman neschimbate (nu apar in taModificari) ==='
|
|
DO TestSumeZero
|
|
|
|
DO PrLogLocal WITH '=== TEST 1: cbc:Amount in PaymentTerms NU se neaga (doar Amount/BaseAmount sub AllowanceCharge) ==='
|
|
DO TestPaymentTermsNeatins
|
|
|
|
DO PrLogLocal WITH '=== TEST: motiv cu caractere speciale (& si <) scris corect prin DOM, nu concatenare ==='
|
|
DO TestMotivSpecial
|
|
|
|
DO PrLogLocal WITH '=== TEST: numar nou cu slash pastrat exact (scris prin DOM) ==='
|
|
DO TestNumarCuSlash
|
|
|
|
DO PrLogLocal WITH '=== TEST 3b: blocaje fara retea (in interiorul StornoEFacturaXML) ==='
|
|
DO TestBlocaje
|
|
|
|
DO PrLogLocal WITH '=== TEST: fixtura B2C (CNP 13 cifre) si furnizor fara PartyTaxScheme - parsare OK, pregatite pentru Lane 2/C ==='
|
|
DO TestRoundTrip WITH 'storno_fixture_b2c_cnp.xml', 'b2c_cnp', .F.
|
|
DO TestRoundTrip WITH 'storno_fixture_fara_partytaxscheme.xml', 'fara_partytaxscheme', .F.
|
|
|
|
DO PrLogLocal WITH '=== TEST: rutare CN/FCN si B2C (logica SendEfactura/ValidareEFacturaANAF, fara apel retea) ==='
|
|
DO TestRutareCNSiB2C
|
|
|
|
DO PrLogLocal WITH '=== TEST 3: validare online ANAF TEST - neexecutat (fara token de test verificat; risc cunoscut SET STEP ON anaf_efactura.prg:717 cu token expirat) ==='
|
|
|
|
DO PrLogLocal WITH '=== TEST 4: upload real ANAF - INTERZIS pentru Lane 1, neexecutat ==='
|
|
|
|
DO PrLogLocal WITH 'TOTAL: PASS=' + TRANSFORM(lnPass) + ' FAIL=' + TRANSFORM(lnFail)
|
|
DO PrLogLocal WITH 'TOTAL: test terminat fara exceptie'
|
|
CATCH TO loEx
|
|
DO PrLogLocal WITH 'EXCEPTIE: ' + loEx.Message + ' (' + TRANSFORM(loEx.LineNo) + ') ' + loEx.Procedure
|
|
ENDTRY
|
|
|
|
QUIT
|
|
|
|
**********************************************************
|
|
* neaga de doua ori (storno al storno-ului) si verifica identitatea: valorile noi din a doua
|
|
* trecere trebuie sa fie exact valorile originale din prima trecere (textual, fara rotunjire).
|
|
* Foloseste taModificari (nu re-parseaza XML-ul), deci verifica direct contractul functiei.
|
|
PROCEDURE TestRoundTrip
|
|
LPARAMETERS tcFixtura, tcLabel, tlVerificaDiacritice
|
|
|
|
LOCAL lcXml, lcFile1, lcFile2, lcMsg1, lcMsg2, llRet1, llRet2
|
|
LOCAL laMod1[1,4], laMod2[1,4]
|
|
|
|
IF !FILE(gcFixDir + tcFixtura)
|
|
DO Assert WITH .F., '[' + tcLabel + '] fixtura exista', gcFixDir + tcFixtura
|
|
RETURN
|
|
ENDIF
|
|
lcXml = FILETOSTR(gcFixDir + tcFixtura)
|
|
lcFile1 = gcOutDir + tcLabel + '_storno1.xml'
|
|
lcFile2 = gcOutDir + tcLabel + '_storno2.xml'
|
|
|
|
llRet1 = StornoEFacturaXML(m.lcXml, '', 'Stornare factura test ' + m.tcLabel, m.lcFile1, @laMod1, @lcMsg1)
|
|
DO Assert WITH llRet1, '[' + tcLabel + '] StornoEFacturaXML (prima trecere) reuseste', 'mesaj=[' + lcMsg1 + ']'
|
|
IF !llRet1
|
|
RETURN
|
|
ENDIF
|
|
DO Assert WITH FILE(m.lcFile1), '[' + tcLabel + '] fisierul storno1 exista pe disc', m.lcFile1
|
|
|
|
IF m.tlVerificaDiacritice
|
|
LOCAL lcBrutOriginal, lcBrutStorno
|
|
lcBrutOriginal = FILETOSTR(gcFixDir + tcFixtura)
|
|
lcBrutStorno = FILETOSTR(m.lcFile1)
|
|
*-- Constan[C5 A3]a (U+0163, "t cu virgula") pastrat identic pe octeti in storno
|
|
DO Assert WITH ('Constan' + CHR(0xC5) + CHR(0xA3) + 'a') $ m.lcBrutStorno, '[' + tcLabel + '] 1b: diacritice C5 A3 (U+0163) pastrate pe octeti in storno1', 'len=' + TRANSFORM(LEN(m.lcBrutStorno))
|
|
ENDIF
|
|
|
|
llRet2 = StornoEFacturaXML(FILETOSTR(m.lcFile1), 'ORIG-TEST-' + tcLabel, '', m.lcFile2, @laMod2, @lcMsg2)
|
|
DO Assert WITH llRet2, '[' + tcLabel + '] StornoEFacturaXML (a doua trecere, pe storno1) reuseste', 'mesaj=[' + lcMsg2 + ']'
|
|
IF !llRet2
|
|
RETURN
|
|
ENDIF
|
|
|
|
*-- filtreaza doar randurile de sume/cantitati (tip 2 total, tip 3 linie), fara antet (tip 1)
|
|
LOCAL lnI, lnJ1, lnJ2
|
|
DIMENSION laF1[1], laF2[1]
|
|
lnJ1 = 0
|
|
FOR lnI = 1 TO ALEN(laMod1, 1)
|
|
IF laMod1[lnI, 4] = 2 OR laMod1[lnI, 4] = 3
|
|
lnJ1 = lnJ1 + 1
|
|
DIMENSION laF1[lnJ1, 2]
|
|
laF1[lnJ1, 1] = laMod1[lnI, 2]
|
|
laF1[lnJ1, 2] = laMod1[lnI, 3]
|
|
ENDIF
|
|
ENDFOR
|
|
lnJ2 = 0
|
|
FOR lnI = 1 TO ALEN(laMod2, 1)
|
|
IF laMod2[lnI, 4] = 2 OR laMod2[lnI, 4] = 3
|
|
lnJ2 = lnJ2 + 1
|
|
DIMENSION laF2[lnJ2, 2]
|
|
laF2[lnJ2, 1] = laMod2[lnI, 2]
|
|
laF2[lnJ2, 2] = laMod2[lnI, 3]
|
|
ENDIF
|
|
ENDFOR
|
|
|
|
DO Assert WITH lnJ1 = lnJ2 AND lnJ1 > 0, '[' + tcLabel + '] 1: numarul de noduri numerice negate e acelasi la dus si intors', 'trecere1=' + TRANSFORM(lnJ1) + ' trecere2=' + TRANSFORM(lnJ2)
|
|
|
|
IF lnJ1 = lnJ2 AND lnJ1 > 0
|
|
LOCAL llToateOk
|
|
llToateOk = .T.
|
|
FOR lnI = 1 TO lnJ1
|
|
*-- valoarea noua din trecerea 1 (negata) trebuie sa fie valoarea originala din trecerea 2
|
|
IF ALLTRIM(laF1[lnI, 2]) <> ALLTRIM(laF2[lnI, 1])
|
|
llToateOk = .F.
|
|
ENDIF
|
|
*-- valoarea noua din trecerea 2 (negata a doua oara) trebuie sa fie exact valoarea originala din trecerea 1
|
|
IF ALLTRIM(laF1[lnI, 1]) <> ALLTRIM(laF2[lnI, 2])
|
|
llToateOk = .F.
|
|
ENDIF
|
|
ENDFOR
|
|
DO Assert WITH llToateOk, '[' + tcLabel + '] 1: dubla negare da exact valorile originale (textual)', ''
|
|
ENDIF
|
|
|
|
*-- 2: un nod static, neatins de negare, ramane identic intre original si storno1
|
|
LOCAL loDOMOrig, loDOMStorno, loNodOrig, loNodStorno
|
|
loDOMOrig = CREATEOBJECT('MSXML2.DOMDocument.6.0')
|
|
loDOMOrig.Async = .F.
|
|
loDOMOrig.setProperty('SelectionLanguage', 'XPath')
|
|
loDOMOrig.LoadXML(m.lcXml)
|
|
loDOMStorno = CREATEOBJECT('MSXML2.DOMDocument.6.0')
|
|
loDOMStorno.Async = .F.
|
|
loDOMStorno.setProperty('SelectionLanguage', 'XPath')
|
|
loDOMStorno.Load(m.lcFile1)
|
|
|
|
loNodOrig = loDOMOrig.documentElement.selectSingleNode("//*[local-name()='RegistrationName']")
|
|
loNodStorno = loDOMStorno.documentElement.selectSingleNode("//*[local-name()='RegistrationName']")
|
|
IF !ISNULL(m.loNodOrig) AND !ISNULL(m.loNodStorno)
|
|
DO Assert WITH loNodOrig.Text == loNodStorno.Text, '[' + tcLabel + '] 2: RegistrationName (nod neatins) identic in storno', '[' + loNodOrig.Text + '] vs [' + loNodStorno.Text + ']'
|
|
ENDIF
|
|
|
|
*-- ID radacinii trebuie sa fie exact numarul cerut (implicit + '-S')
|
|
LOCAL loIdOrig, loIdStorno
|
|
loIdOrig = loDOMOrig.documentElement.selectSingleNode("*[local-name()='ID']")
|
|
loIdStorno = loDOMStorno.documentElement.selectSingleNode("*[local-name()='ID']")
|
|
IF !ISNULL(m.loIdOrig) AND !ISNULL(m.loIdStorno)
|
|
DO Assert WITH ALLTRIM(loIdStorno.Text) == ALLTRIM(loIdOrig.Text) + '-S', '[' + tcLabel + '] numar storno implicit = numar original + -S', '[' + loIdStorno.Text + ']'
|
|
ENDIF
|
|
|
|
*-- BillingReference catre originalul (numarul VECHI, dinainte de schimbare)
|
|
LOCAL loRef
|
|
loRef = loDOMStorno.documentElement.selectSingleNode("//*[local-name()='BillingReference']/*[local-name()='InvoiceDocumentReference']/*[local-name()='ID']")
|
|
IF !ISNULL(m.loRef) AND !ISNULL(m.loIdOrig)
|
|
DO Assert WITH ALLTRIM(loRef.Text) == ALLTRIM(loIdOrig.Text), '[' + tcLabel + '] BillingReference contine numarul original', '[' + loRef.Text + ']'
|
|
ELSE
|
|
DO Assert WITH .F., '[' + tcLabel + '] BillingReference/InvoiceDocumentReference/ID gasit', ''
|
|
ENDIF
|
|
|
|
*-- Nota scrisa (a fost trimisa nevida la trecerea 1)
|
|
LOCAL loNote
|
|
loNote = loDOMStorno.documentElement.selectSingleNode("*[local-name()='Note']")
|
|
IF !ISNULL(m.loNote)
|
|
DO Assert WITH 'Stornare factura test' $ loNote.Text, '[' + tcLabel + '] Nota scrisa pe document', '[' + loNote.Text + ']'
|
|
ELSE
|
|
DO Assert WITH .F., '[' + tcLabel + '] Nota (cbc:Note) gasita dupa storno', ''
|
|
ENDIF
|
|
ENDPROC
|
|
|
|
**********************************************************
|
|
* namespace-ul nodurilor NOI (BillingReference/ID, Note) trebuie sa fie cel din radacina
|
|
* originalului (n1/CommonBasicComponents-2, n2/CommonAggregateComponents-2), nu prefix implicit
|
|
PROCEDURE TestNamespaceN3
|
|
LOCAL lcFile1, loDOM, loRef, loNote
|
|
LOCAL lcUriCbc, lcUriCac
|
|
lcUriCbc = 'urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2'
|
|
lcUriCac = 'urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2'
|
|
lcFile1 = gcOutDir + 'prefix_n3_storno1.xml'
|
|
IF !FILE(m.lcFile1)
|
|
DO Assert WITH .F., '[prefix_n3] 1c: fisierul storno1 exista (rulat de TestRoundTrip)', m.lcFile1
|
|
RETURN
|
|
ENDIF
|
|
|
|
loDOM = CREATEOBJECT('MSXML2.DOMDocument.6.0')
|
|
loDOM.Async = .F.
|
|
loDOM.setProperty('SelectionLanguage', 'XPath')
|
|
loDOM.Load(m.lcFile1)
|
|
|
|
loRef = loDOM.documentElement.selectSingleNode("//*[local-name()='BillingReference']")
|
|
IF !ISNULL(m.loRef)
|
|
DO Assert WITH loRef.namespaceURI == lcUriCac, '[prefix_n3] 1c: BillingReference in namespace cac corect', loRef.namespaceURI
|
|
ELSE
|
|
DO Assert WITH .F., '[prefix_n3] 1c: BillingReference gasit', ''
|
|
ENDIF
|
|
|
|
LOCAL loRefId
|
|
loRefId = loDOM.documentElement.selectSingleNode("//*[local-name()='BillingReference']/*[local-name()='InvoiceDocumentReference']/*[local-name()='ID']")
|
|
IF !ISNULL(m.loRefId)
|
|
DO Assert WITH loRefId.namespaceURI == lcUriCbc, '[prefix_n3] 1c: ID din InvoiceDocumentReference in namespace cbc corect', loRefId.namespaceURI
|
|
ELSE
|
|
DO Assert WITH .F., '[prefix_n3] 1c: BillingReference/InvoiceDocumentReference/ID gasit', ''
|
|
ENDIF
|
|
|
|
loNote = loDOM.documentElement.selectSingleNode("*[local-name()='Note']")
|
|
IF !ISNULL(m.loNote)
|
|
DO Assert WITH loNote.namespaceURI == lcUriCbc, '[prefix_n3] 1c: Note in namespace cbc corect', loNote.namespaceURI
|
|
ELSE
|
|
DO Assert WITH .F., '[prefix_n3] 1c: Note gasita', ''
|
|
ENDIF
|
|
ENDPROC
|
|
|
|
**********************************************************
|
|
* -0.00 si 0.00 nu trebuie sa apara deloc in taModificari (raman neschimbate, cf. spec)
|
|
PROCEDURE TestSumeZero
|
|
LOCAL lcXml, lcFile, lcMsg, llRet
|
|
LOCAL laMod[1,4]
|
|
lcXml = FILETOSTR(gcFixDir + 'storno_fixture_sume_zero.xml')
|
|
lcFile = gcOutDir + 'sume_zero_storno1.xml'
|
|
llRet = StornoEFacturaXML(m.lcXml, '', '', m.lcFile, @laMod, @lcMsg)
|
|
DO Assert WITH llRet, '[sume_zero] StornoEFacturaXML reuseste', 'mesaj=[' + lcMsg + ']'
|
|
IF !llRet
|
|
RETURN
|
|
ENDIF
|
|
|
|
LOCAL lnI, llGasitZero
|
|
llGasitZero = .F.
|
|
FOR lnI = 1 TO ALEN(laMod, 1)
|
|
IF (laMod[lnI, 4] = 2 OR laMod[lnI, 4] = 3) AND ('0.00' $ laMod[lnI, 2] OR '0,00' $ laMod[lnI, 2])
|
|
IF VAL(STRTRAN(ALLTRIM(laMod[lnI, 2]), '-', '')) = 0
|
|
llGasitZero = .T.
|
|
ENDIF
|
|
ENDIF
|
|
ENDFOR
|
|
DO Assert WITH !llGasitZero, '[sume_zero] nicio valoare zero (inclusiv -0.00) nu apare in taModificari', 'randuri=' + TRANSFORM(ALEN(laMod,1))
|
|
|
|
*-- verificare directa pe fisier: -0.00 din AllowanceCharge/Amount ramane neschimbat
|
|
LOCAL lcOut
|
|
lcOut = FILETOSTR(m.lcFile)
|
|
DO Assert WITH '-0.00' $ m.lcOut, '[sume_zero] -0.00 (AllowanceCharge/Amount) prezent neschimbat in storno', ''
|
|
ENDPROC
|
|
|
|
**********************************************************
|
|
* cbc:Amount din cac:PaymentTerms nu are parintele AllowanceCharge -> nu se neaga
|
|
PROCEDURE TestPaymentTermsNeatins
|
|
LOCAL lcXml, lcFile, lcMsg, llRet
|
|
LOCAL laMod[1,4]
|
|
lcXml = FILETOSTR(gcFixDir + 'storno_fixture_paymentterms.xml')
|
|
lcFile = gcOutDir + 'paymentterms_storno1.xml'
|
|
llRet = StornoEFacturaXML(m.lcXml, '', '', m.lcFile, @laMod, @lcMsg)
|
|
DO Assert WITH llRet, '[paymentterms] StornoEFacturaXML reuseste', 'mesaj=[' + lcMsg + ']'
|
|
IF !llRet
|
|
RETURN
|
|
ENDIF
|
|
|
|
LOCAL loDOM, loPT
|
|
loDOM = CREATEOBJECT('MSXML2.DOMDocument.6.0')
|
|
loDOM.Async = .F.
|
|
loDOM.setProperty('SelectionLanguage', 'XPath')
|
|
loDOM.Load(m.lcFile)
|
|
loPT = loDOM.documentElement.selectSingleNode("//*[local-name()='PaymentTerms']/*[local-name()='Amount']")
|
|
IF !ISNULL(m.loPT)
|
|
DO Assert WITH ALLTRIM(loPT.Text) == '119.00', '[paymentterms] cbc:Amount din PaymentTerms ramane neatins (119.00)', '[' + loPT.Text + ']'
|
|
ELSE
|
|
DO Assert WITH .F., '[paymentterms] nodul PaymentTerms/Amount gasit', ''
|
|
ENDIF
|
|
ENDPROC
|
|
|
|
**********************************************************
|
|
* Nota cu '&' si '<' scrisa prin .Text (DOM face escape automat) - XML-ul ramane valid
|
|
PROCEDURE TestMotivSpecial
|
|
LOCAL lcXml, lcFile, lcMsg, llRet, lcNota
|
|
LOCAL laMod[1,4]
|
|
lcXml = FILETOSTR(gcFixDir + 'storno_fixture_diacritice_reale.xml')
|
|
lcFile = gcOutDir + 'motiv_special_storno1.xml'
|
|
lcNota = 'Marfa < prag & retur client'
|
|
llRet = StornoEFacturaXML(m.lcXml, '', m.lcNota, m.lcFile, @laMod, @lcMsg)
|
|
DO Assert WITH llRet, '[motiv_special] StornoEFacturaXML reuseste cu motiv & si <', 'mesaj=[' + lcMsg + ']'
|
|
IF !llRet
|
|
RETURN
|
|
ENDIF
|
|
|
|
LOCAL loDOM, loNote
|
|
loDOM = CREATEOBJECT('MSXML2.DOMDocument.6.0')
|
|
loDOM.Async = .F.
|
|
loDOM.setProperty('SelectionLanguage', 'XPath')
|
|
loDOM.Load(m.lcFile)
|
|
DO Assert WITH loDOM.parseError.errorcode = 0, '[motiv_special] xml-ul rezultat e valid (parseError=0)', TRANSFORM(loDOM.parseError.errorcode)
|
|
loNote = loDOM.documentElement.selectSingleNode("*[local-name()='Note']")
|
|
IF !ISNULL(m.loNote)
|
|
DO Assert WITH loNote.Text == m.lcNota, '[motiv_special] textul notei intact dupa reincarcare (& si < pastrate ca text)', '[' + loNote.Text + ']'
|
|
ELSE
|
|
DO Assert WITH .F., '[motiv_special] Note gasita', ''
|
|
ENDIF
|
|
ENDPROC
|
|
|
|
**********************************************************
|
|
* numar cu '/' (documente cu numerotare pe serie/numar) scris prin .Text, fara conflict XML
|
|
PROCEDURE TestNumarCuSlash
|
|
LOCAL lcXml, lcFile, lcMsg, llRet, lcNumar
|
|
LOCAL laMod[1,4]
|
|
lcXml = FILETOSTR(gcFixDir + 'storno_fixture_diacritice_reale.xml')
|
|
lcFile = gcOutDir + 'numar_slash_storno1.xml'
|
|
lcNumar = 'FF/2026/S'
|
|
llRet = StornoEFacturaXML(m.lcXml, m.lcNumar, '', m.lcFile, @laMod, @lcMsg)
|
|
DO Assert WITH llRet, '[numar_slash] StornoEFacturaXML reuseste cu numar continand /', 'mesaj=[' + lcMsg + ']'
|
|
IF !llRet
|
|
RETURN
|
|
ENDIF
|
|
|
|
LOCAL loDOM, loId
|
|
loDOM = CREATEOBJECT('MSXML2.DOMDocument.6.0')
|
|
loDOM.Async = .F.
|
|
loDOM.setProperty('SelectionLanguage', 'XPath')
|
|
loDOM.Load(m.lcFile)
|
|
loId = loDOM.documentElement.selectSingleNode("*[local-name()='ID']")
|
|
IF !ISNULL(m.loId)
|
|
DO Assert WITH loId.Text == m.lcNumar, '[numar_slash] ID radacinii = numarul cu / exact', '[' + loId.Text + ']'
|
|
ELSE
|
|
DO Assert WITH .F., '[numar_slash] ID gasit', ''
|
|
ENDIF
|
|
ENDPROC
|
|
|
|
**********************************************************
|
|
* blocaje interne StornoEFacturaXML, fara nicio conexiune de retea
|
|
PROCEDURE TestBlocaje
|
|
LOCAL lcMsg, llRet
|
|
LOCAL laMod[1,4]
|
|
|
|
llRet = StornoEFacturaXML('', '', '', gcOutDir + 'blocaj_gol.xml', @laMod, @lcMsg)
|
|
DO Assert WITH !llRet AND 'gol' $ LOWER(lcMsg), '3b: XML gol -> blocare cu mesaj clar', '[' + lcMsg + ']'
|
|
|
|
llRet = StornoEFacturaXML('<Invoice><nchis', '', '', gcOutDir + 'blocaj_malformat.xml', @laMod, @lcMsg)
|
|
DO Assert WITH !llRet AND 'pars' $ LOWER(lcMsg), '3b: XML malformat -> eroare de parsare', '[' + lcMsg + ']'
|
|
|
|
llRet = StornoEFacturaXML('<Altceva xmlns="x"><y>1</y></Altceva>', '', '', gcOutDir + 'blocaj_radacina.xml', @laMod, @lcMsg)
|
|
DO Assert WITH !llRet AND 'Invoice/CreditNote' $ lcMsg, '3b: radacina ne-Invoice/CreditNote -> blocare cu mesaj clar', '[' + lcMsg + ']'
|
|
|
|
llRet = StornoEFacturaXML('<Invoice xmlns:cbc="u1" xmlns:cac="u2"><cbc:DueDate>2026-01-01</cbc:DueDate></Invoice>', '', '', gcOutDir + 'blocaj_lipsa.xml', @laMod, @lcMsg)
|
|
DO Assert WITH !llRet AND 'Lipseste' $ lcMsg, '3b: lipseste ID/IssueDate/Supplier -> blocare cu mesaj clar', '[' + lcMsg + ']'
|
|
|
|
LOCAL lcXmlBadNum
|
|
lcXmlBadNum = STRTRAN(FILETOSTR(gcFixDir + 'storno_fixture_diacritice_reale.xml'), '<cbc:TaxAmount currencyID="RON">18.18</cbc:TaxAmount>', '<cbc:TaxAmount currencyID="RON">ABC</cbc:TaxAmount>', 1, 1)
|
|
DO Assert WITH 'ABC' $ lcXmlBadNum, '3b: pregatire fixtura cu valoare nenumerica (substitutie reusita)', ''
|
|
llRet = StornoEFacturaXML(m.lcXmlBadNum, '', '', gcOutDir + 'blocaj_nenumeric.xml', @laMod, @lcMsg)
|
|
DO Assert WITH !llRet AND 'nenumeric' $ LOWER(lcMsg) AND 'TaxAmount' $ lcMsg, '3b: valoare nenumerica -> blocare, mesajul numeste nodul', '[' + lcMsg + ']'
|
|
|
|
DO PrLogLocal WITH ' (CIF strain / luna blocata: verificari la pasul 2 in frm_storno_efactura, Lane 2/C - nu exista inca cod de testat)'
|
|
ENDPROC
|
|
|
|
**********************************************************
|
|
* rutarea CN/FCN (standard=CN la upload, FCN la validare) si detectia B2C (CNP 13 cifre) -
|
|
* aceeasi expresie ca in SendEfactura/ValidareEFacturaANAF, verificata fara HTTP
|
|
PROCEDURE TestRutareCNSiB2C
|
|
LOCAL lcInv, lcCn, lcB2c
|
|
lcInv = FILETOSTR(gcFixDir + 'storno_fixture_diacritice_reale.xml')
|
|
lcCn = FILETOSTR(gcFixDir + 'storno_fixture_creditnote_minimal.xml')
|
|
lcB2c = FILETOSTR(gcFixDir + 'storno_fixture_b2c_cnp.xml')
|
|
|
|
DO Assert WITH IIF('CreditNote' $ lcInv, 'CN', 'UBL') == 'UBL', 'rutare: Invoice -> standard=UBL / validare FACT1', ''
|
|
DO Assert WITH IIF('CreditNote' $ lcCn, 'CN', 'UBL') == 'CN', 'rutare: CreditNote -> standard=CN', ''
|
|
DO Assert WITH IIF('CreditNote' $ lcCn, 'FCN', 'FACT1') == 'FCN', 'rutare: CreditNote -> validare FCN', ''
|
|
DO Assert WITH IIF('CreditNote' $ lcInv, 'FCN', 'FACT1') == 'FACT1', 'rutare: Invoice -> validare FACT1', ''
|
|
|
|
LOCAL lcCustomer, lcPartyLegal, lcCodFiscalClient
|
|
lcCustomer = STREXTRACT(lcB2c, '<cac:AccountingCustomerParty>', '</cac:AccountingCustomerParty>', 1, 1)
|
|
lcPartyLegal = STREXTRACT(lcCustomer, '<cac:PartyLegalEntity>', '</cac:PartyLegalEntity>', 1, 1)
|
|
lcCodFiscalClient = ALLTRIM(STREXTRACT(lcPartyLegal, '<cbc:CompanyID>', '</cbc:CompanyID>', 1, 1))
|
|
DO Assert WITH LEN(lcCodFiscalClient) = 13, 'B2C: CNP de 13 cifre detectat din fixtura (llB2C ar deveni .T.)', 'cnp=[' + lcCodFiscalClient + '] len=' + TRANSFORM(LEN(lcCodFiscalClient))
|
|
ENDPROC
|
|
|
|
**********************************************************
|
|
PROCEDURE Assert
|
|
LPARAMETERS tlCond, tcTitlu, tcDetaliu
|
|
IF tlCond
|
|
lnPass = lnPass + 1
|
|
DO PrLogLocal WITH ' OK ' + tcTitlu + IIF(EMPTY(tcDetaliu), '', ' (' + tcDetaliu + ')')
|
|
ELSE
|
|
lnFail = lnFail + 1
|
|
DO PrLogLocal WITH ' FAIL ' + tcTitlu + IIF(EMPTY(tcDetaliu), '', ' (' + tcDetaliu + ')')
|
|
ENDIF
|
|
ENDPROC
|
|
|
|
**********************************************************
|
|
PROCEDURE PrLogLocal
|
|
LPARAMETERS tcMsg
|
|
LOCAL lcL
|
|
SET SAFETY OFF
|
|
lcL = IIF(FILE(gcUILog), FILETOSTR(gcUILog), '')
|
|
STRTOFILE(lcL + TRANSFORM(tcMsg) + CHR(13) + CHR(10), gcUILog)
|
|
ENDPROC
|
|
|
|
**********************************************************
|
|
PROCEDURE PrErrLocal
|
|
LPARAMETERS tnError, tcMessage, tcProgram, tnLineNo
|
|
DO PrLogLocal WITH 'EROARE ' + TRANSFORM(tnError) + ' ' + TRANSFORM(tcMessage) + ' in ' + TRANSFORM(tcProgram) + ' linia ' + TRANSFORM(tnLineNo)
|
|
ENDPROC
|