sync SVN r18181

This commit is contained in:
2026-09-18 19:48:19 +03:00
parent 79214c44ea
commit 45aad0f07c
5 changed files with 1122 additions and 16 deletions

View File

@@ -377,6 +377,7 @@ DEFINE CLASS anaf_efactura AS _frmbase OF "_frm_base.vcx"
*m: bifeazatrimismanual && Bifeaza o factura ca trimisa manual
*m: citesteraspunsuri
*m: do_ataseaza
*m: do_storno_efactura
*m: gettoken
*m: ltest_assign
*m: refreshdetaliifacturi
@@ -5656,7 +5657,7 @@ DEFINE CLASS anaf_efactura AS _frmbase OF "_frm_base.vcx"
lcCursor = 'crsFacturiEmise'
CASE m.lnPaginaActiva = 2
* Facturi TRIMISE
lnOptiune = xmenu('Centralizator facturi;Lista facturi trimise - detaliat;Factura trimisa curenta;\-;PDF ANAF facturi alese;PDF ANAF factura curenta;\-;Editare in browser')
lnOptiune = xmenu('Centralizator facturi;Lista facturi trimise - detaliat;Factura trimisa curenta;\-;PDF ANAF facturi alese;PDF ANAF factura curenta;\-;Editare in browser;\-;Generare XML storno si trimitere la ANAF')
lcCursor = 'crsFacturiTrimise'
CASE m.lnPaginaActiva = 3
* Facturi PRIMITE
@@ -5822,11 +5823,15 @@ DEFINE CLASS anaf_efactura AS _frmbase OF "_frm_base.vcx"
loSender = NEWOBJECT("eFacturaXmlPreview", "xmlefactura-preview.prg")
loSender.webURL = "https://romfast.ro/efactura-generator/"
loSender.SendXML(m.lcDetalii, m.llVariable)
CASE m.lnOptiune = 10 AND m.lnPaginaActiva = 2
* Generare XML storno si trimitere la ANAF - doar pagina Trimise (D6)
This.do_storno_efactura()
Endcase
Select (m.lcSelect)
ENDPROC
PROCEDURE do_sterge
@@ -5877,6 +5882,144 @@ DEFINE CLASS anaf_efactura AS _frmbase OF "_frm_base.vcx"
ENDPROC
PROCEDURE do_storno_efactura
*!* 18.09.2026 stornare XML eFactura din borderou (docs\plan_storno_efactura.md)
LOCAL lnId, lcXmlOriginal, lcMesaj, lnRecno, lcNumarOriginal, ldDataOriginal
LOCAL lcClient, lcMoneda, lnTotal, lnTestRand, lcCifFirma, lcTempXml, loDom
LOCAL lcCifXml, loNod, lcIssueDateStr, ldIssueDate, lcCustomerCif, llB2C, loNoduri, lnI
LOCAL lcNumarNou, lcNumarNouSafe, lcFisierStorno, lcNotaIni, llSucces
LOCAL laModificari[1], lcAvertizare, laDubl[1], laProcesate[1], loForma
IF AMESSAGEBOX('Se genereaza XML-ul de stornare al facturii selectate (aceleasi linii, valori negative).' + CHR(13) + CHR(13) + ;
'Nu se trimite nimic la ANAF pana nu verificati XML-ul in fereastra urmatoare si apasati "Trimite la ANAF".', 1+64, _screen.Caption) <> 1
RETURN
ENDIF
SELECT crsFacturiTrimise
lnRecno = Recno()
IF ALLTRIM(NVL(tip_mesaj_raspuns, '')) <> 'FACTURA TRIMISA'
AMESSAGEBOX('Stornarea este posibila doar pentru facturi acceptate (FACTURA TRIMISA).', 0+48, _screen.Caption)
RETURN
ENDIF
lnId = NVL(crsFacturiTrimise.Id, 0)
IF Empty(m.lnId)
RETURN
ENDIF
lcNumarOriginal = ALLTRIM(NVL(crsFacturiTrimise.numar_act, ''))
lcClient = ALLTRIM(NVL(crsFacturiTrimise.xclient, ''))
lcMoneda = ALLTRIM(NVL(crsFacturiTrimise.nume_valuta, ''))
lnTotal = NVL(crsFacturiTrimise.total_cu_tva, 0)
lnTestRand = NVL(crsFacturiTrimise.test, 0)
lcCifFirma = ALLTRIM(Thisform.oAnaf.AnafeFacturaServer.cCodFiscal)
IF lnTestRand <> IIF(Thisform.oAnaf.AnafeFacturaServer.lTest, 1, 0)
AMESSAGEBOX('Documentul original e din mediul ' + IIF(m.lnTestRand=1,'TEST','PRODUCTIE') + ;
', dar borderoul este pe ' + IIF(Thisform.oAnaf.AnafeFacturaServer.lTest,'TEST','PRODUCTIE') + '. Comutati mediul inainte de stornare.', 0+16, _screen.Caption)
RETURN
ENDIF
IF !GetXmlOriginalEFactura(m.lnId, @lcXmlOriginal, @lcMesaj)
AMESSAGEBOX('Factura nu are XML descarcat: ' + m.lcMesaj, 0+16, _screen.Caption)
RETURN
ENDIF
* parsez XML-ul pe fisier (nu .loadXML pe sir), ca sa nu risc reinterpretarea octetilor UTF-8
lcTempXml = Addbs(Sys(2023)) + 'storno_check_' + Sys(2015) + '.xml'
Strtofile(m.lcXmlOriginal, m.lcTempXml)
loDom = Createobject('MSXML2.DOMDocument.6.0')
loDom.setProperty('SelectionLanguage', 'XPath')
loDom.preserveWhiteSpace = .T.
llSucces = loDom.load(m.lcTempXml)
TRY
Delete File (m.lcTempXml)
CATCH
ENDTRY
IF !m.llSucces OR !Inlist(loDom.documentElement.baseName, 'Invoice', 'CreditNote')
AMESSAGEBOX('Documentul nu e Invoice/CreditNote.', 0+16, _screen.Caption)
RETURN
ENDIF
*!* 18.09.2026 CIF-ul poate fi in PartyTaxScheme sau PartyLegalEntity (acolo e uneori nr. Reg. Com.)
lcCifXml = ''
loNoduri = loDom.selectNodes("//*[local-name()='AccountingSupplierParty']//*[local-name()='CompanyID']")
FOR lnI = 0 TO m.loNoduri.length - 1
loNod = m.loNoduri.item(m.lnI)
lcCifXml = GetNrFromString(ALLTRIM(m.loNod.Text))
IF m.lcCifXml == ALLTRIM(m.lcCifFirma)
EXIT
ENDIF
ENDFOR
IF Empty(m.lcCifXml) OR !(m.lcCifXml == ALLTRIM(m.lcCifFirma))
AMESSAGEBOX('Factura e emisa de alta firma (CIF ' + m.lcCifXml + ').', 0+16, _screen.Caption)
RETURN
ENDIF
loNod = loDom.selectSingleNode("//*[local-name()='IssueDate']")
lcIssueDateStr = IIF(Isnull(m.loNod), '', ALLTRIM(m.loNod.Text))
ldIssueDate = IIF(Len(m.lcIssueDateStr) < 10, {}, Date(Val(Substr(m.lcIssueDateStr,1,4)), Val(Substr(m.lcIssueDateStr,6,2)), Val(Substr(m.lcIssueDateStr,9,2))))
IF Empty(m.ldIssueDate)
AMESSAGEBOX('Nu s-a putut citi data facturii din XML.', 0+16, _screen.Caption)
RETURN
ENDIF
IF is_luna_blocata(gcS, gnId_Prg_Owner, Year(m.ldIssueDate), Month(m.ldIssueDate))
AMESSAGEBOX('Luna ' + Padl(Month(m.ldIssueDate),2,'0') + '.' + Transform(Year(m.ldIssueDate)) + ' este inchisa.', 0+16, _screen.Caption)
RETURN
ENDIF
loNod = loDom.selectSingleNode("//*[local-name()='AccountingCustomerParty']//*[local-name()='PartyLegalEntity']/*[local-name()='CompanyID']")
lcCustomerCif = IIF(Isnull(m.loNod), '', ALLTRIM(m.loNod.Text))
llB2C = (Len(m.lcCustomerCif) = 13)
lcNumarNou = m.lcNumarOriginal + '-S'
lcNumarNouSafe = WindowsSpecialCharacters(m.lcNumarNou)
lcFisierStorno = GetPdfPath('EFACTURA','TRIMISE') + 'storno_' + m.lcNumarNouSafe + '_' + m.lcCifFirma + '.xml'
lcNotaIni = 'Stornare factura ' + m.lcNumarOriginal + ' din data ' + Dtoc(m.ldIssueDate)
llSucces = StornoEFacturaXML(m.lcXmlOriginal, m.lcNumarNou, m.lcNotaIni, m.lcFisierStorno, @laModificari, @lcMesaj)
IF !m.llSucces
AMESSAGEBOX('Nu s-a putut genera XML-ul de stornare: ' + m.lcMesaj, 0+16, _screen.Caption)
RETURN
ENDIF
* Avertizari (nu blocheaza): termen transmitere, storno dublu, storno deja trimis local, sursa CreditNote/negativa (D13)
lcAvertizare = ''
IF (Date() - m.ldIssueDate) > 5
lcAvertizare = m.lcAvertizare + 'Au trecut mai mult de 5 zile de la data facturii (' + Dtoc(m.ldIssueDate) + ') - verificati termenul de transmitere la ANAF.' + Chr(13) + Chr(10)
ENDIF
SELECT Count(*) as nCnt FROM crsFacturiTrimise WHERE Alltrim(numar_act) == m.lcNumarNou INTO CURSOR cVerifDubl
IF cVerifDubl.nCnt > 0
lcAvertizare = m.lcAvertizare + 'Exista deja un document ' + m.lcNumarNou + ' in Trimise - posibila stornare dubla.' + Chr(13) + Chr(10)
ENDIF
USE IN (SELECT('cVerifDubl'))
IF !Empty(Adir(laProcesate, GetPdfPath('EFACTURA','TRIMISE') + 'procesat\*_storno_' + m.lcNumarNouSafe + '_' + m.lcCifFirma + '*.xml'))
lcAvertizare = m.lcAvertizare + 'Un storno cu acest numar a fost deja trimis de pe acest calculator.' + Chr(13) + Chr(10)
ENDIF
IF loDom.documentElement.baseName == 'CreditNote' OR m.lnTotal < 0
lcAvertizare = m.lcAvertizare + 'Documentul original e nota de credit / are total negativ - stornarea va produce valori pozitive.' + Chr(13) + Chr(10)
ENDIF
CREATE CURSOR crsStornoDetalii (element C(60), original C(250), nou C(250), tip N(1))
loForma = NEWOBJECT('frm_storno_efactura', 'anaf_efactura.vcx', '', ;
Thisform.oAnaf.AnafeFacturaServer, m.lnId, m.lcXmlOriginal, m.lcNumarOriginal, m.ldIssueDate, ;
m.lcClient, m.lcCifFirma, m.lnTotal, m.lcMoneda, Thisform.oAnaf.AnafeFacturaServer.lTest, m.llB2C, ;
m.lcFisierStorno, m.lcNumarNou, m.lcAvertizare)
loForma.UmpleGrid(@laModificari)
loForma.Show(1)
IF Used('crsStornoDetalii')
USE IN (SELECT('crsStornoDetalii'))
ENDIF
SELECT crsFacturiTrimise
TRY
Goto m.lnRecno
CATCH
ENDTRY
ENDPROC
PROCEDURE do_verifica
Local lcText, lnActivePage
lnActivePage = This.pgfeFactura.ActivePage
@@ -15360,3 +15503,504 @@ DEFINE CLASS frm_import_efactura AS _frmbase OF "_frm_base.vcx"
ENDPROC
ENDDEFINE
DEFINE CLASS frm_storno_efactura AS _frmbase OF "_frm_base.vcx"
*< CLASSDATA: Baseclass="form" Timestamp="" Scale="Pixels" Uniqueid="" />
*-- OBJECTDATA items order determines ZOrder / El orden de los items OBJECTDATA determina el ZOrder
*< OBJECTDATA: ObjPath="But_renunt1" UniqueID="" Timestamp="" />
*< OBJECTDATA: ObjPath="lblAntet" UniqueID="" Timestamp="" />
*< OBJECTDATA: ObjPath="lblNumar" UniqueID="" Timestamp="" />
*< OBJECTDATA: ObjPath="txtNumar" UniqueID="" Timestamp="" />
*< OBJECTDATA: ObjPath="lblMotiv" UniqueID="" Timestamp="" />
*< OBJECTDATA: ObjPath="txtMotiv" UniqueID="" Timestamp="" />
*< OBJECTDATA: ObjPath="lblAvertisment" UniqueID="" Timestamp="" />
*< OBJECTDATA: ObjPath="grdModificari" UniqueID="" Timestamp="" />
*< OBJECTDATA: ObjPath="grdModificari.cElement.Header1" UniqueID="" Timestamp="" />
*< OBJECTDATA: ObjPath="grdModificari.cElement.Text1" UniqueID="" Timestamp="" />
*< OBJECTDATA: ObjPath="grdModificari.cOriginal.Header1" UniqueID="" Timestamp="" />
*< OBJECTDATA: ObjPath="grdModificari.cOriginal.Text1" UniqueID="" Timestamp="" />
*< OBJECTDATA: ObjPath="grdModificari.cNou.Header1" UniqueID="" Timestamp="" />
*< OBJECTDATA: ObjPath="grdModificari.cNou.Text1" UniqueID="" Timestamp="" />
*< OBJECTDATA: ObjPath="lblStare" UniqueID="" Timestamp="" />
*< OBJECTDATA: ObjPath="edtStatus" UniqueID="" Timestamp="" />
*< OBJECTDATA: ObjPath="cmdValideaza" UniqueID="" Timestamp="" />
*< OBJECTDATA: ObjPath="cmdPdf" UniqueID="" Timestamp="" />
*< OBJECTDATA: ObjPath="cmdTrimite" UniqueID="" Timestamp="" />
*<DefinedPropArrayMethod>
*m: enablebutoane
*m: regenereazaxml
*m: setstare
*m: umplegrid
*p: pcciffirma
*p: pcclient
*p: pcfisierstorno
*p: pcmoneda
*p: pcmotiv
*p: pcnumarnou
*p: pcnumaroriginal
*p: pcxmloriginal
*p: pddataoriginal
*p: plb2c
*p: pltest
*p: pnidefactura
*p: pnstare
*p: pntotal
*p: poanaf
*</DefinedPropArrayMethod>
*<PropValue>
BorderStyle = 1
DoCreate = .T.
Height = 560
Name = "frm_storno_efactura"
pcciffirma = ''
pcclient = ''
pcfisierstorno = ''
pcmoneda = ''
pcmotiv = ''
pcnumarnou = ''
pcnumaroriginal = ''
pcxmloriginal = ''
pddataoriginal = {}
plb2c = .F.
pltest = .F.
pnidefactura = 0
pnstare = 0
pntotal = 0
poanaf = .NULL.
Width = 760
_memberdata = <VFPData>
<memberdata name="poanaf" display="oAnaf"/>
<memberdata name="pnidefactura" display="nIdEfactura"/>
<memberdata name="pcxmloriginal" display="cXmlOriginal"/>
<memberdata name="pcnumaroriginal" display="cNumarOriginal"/>
<memberdata name="pddataoriginal" display="dDataOriginal"/>
<memberdata name="pcclient" display="cClient"/>
<memberdata name="pcciffirma" display="cCifFirma"/>
<memberdata name="pntotal" display="nTotal"/>
<memberdata name="pcmoneda" display="cMoneda"/>
<memberdata name="pltest" display="lTest"/>
<memberdata name="plb2c" display="lB2C"/>
<memberdata name="pcfisierstorno" display="cFisierStorno"/>
<memberdata name="pcnumarnou" display="cNumarNou"/>
<memberdata name="pcmotiv" display="cMotiv"/>
<memberdata name="pnstare" display="nStare"/>
<memberdata name="enablebutoane" display="EnableButoane"/>
<memberdata name="regenereazaxml" display="RegenereazaXml"/>
<memberdata name="setstare" display="SetStare"/>
<memberdata name="umplegrid" display="UmpleGrid"/>
</VFPData>
_shape1.Height = 29
_shape1.Left = 0
_shape1.Name = "_shape1"
_shape1.Top = 0
_shape1.Width = 760
_shape2.Left = 701
_shape2.Name = "_shape2"
_shape2.Top = 0
Lb_titlu_alb_b121.Caption = "Stornare eFactura"
Lb_titlu_alb_b121.Name = "Lb_titlu_alb_b121"
BUT_TERMIN1.Name = "BUT_TERMIN1"
BUT_TERMIN1.Visible = .F.
Gridsort1.Name = "Gridsort1"
*</PropValue>
ADD OBJECT 'But_renunt1' AS but_renunt WITH ;
Anchor = 8, ;
Left = 701, ;
Name = "But_renunt1", ;
Top = 2
*< END OBJECT: ClassLib="cmd_butoane.vcx" BaseClass="commandbutton" />
ADD OBJECT 'cmdPdf' AS cmd_executa WITH ;
Caption = "\<PDF ANAF", ;
Height = 30, ;
Left = 160, ;
Name = "cmdPdf", ;
TabIndex = 4, ;
Top = 512, ;
Width = 140
*< END OBJECT: ClassLib="cmd_butoane.vcx" BaseClass="commandbutton" />
ADD OBJECT 'cmdTrimite' AS cmd_executa WITH ;
Caption = "\<Trimite la ANAF", ;
FontBold = .T., ;
ForeColor = 0,128,0, ;
Height = 30, ;
Left = 588, ;
Name = "cmdTrimite", ;
TabIndex = 5, ;
Top = 512, ;
Width = 160
*< END OBJECT: ClassLib="cmd_butoane.vcx" BaseClass="commandbutton" />
ADD OBJECT 'cmdValideaza' AS cmd_executa WITH ;
Caption = "\<Valideaza", ;
Height = 30, ;
Left = 12, ;
Name = "cmdValideaza", ;
TabIndex = 3, ;
Top = 512, ;
Width = 140
*< END OBJECT: ClassLib="cmd_butoane.vcx" BaseClass="commandbutton" />
ADD OBJECT 'edtStatus' AS _editbox WITH ;
Height = 50, ;
Left = 220, ;
Name = "edtStatus", ;
ReadOnly = .T., ;
Top = 448, ;
Width = 528
*< END OBJECT: ClassLib="_baza.vcx" BaseClass="editbox" />
ADD OBJECT 'grdModificari' AS _grdbase WITH ;
ColumnCount = 3, ;
DeleteMark = .F., ;
FontSize = 10, ;
Height = 280, ;
Left = 12, ;
Name = "grdModificari", ;
Panel = 1, ;
ReadOnly = .T., ;
RecordSource = "crsStornoDetalii", ;
RowHeight = 19, ;
TabIndex = 6, ;
Top = 164, ;
Width = 736, ;
Column1.ControlSource = "crsStornoDetalii.element", ;
Column1.FontSize = 10, ;
Column1.Name = "cElement", ;
Column1.ReadOnly = .T., ;
Column1.Width = 300, ;
Column2.Alignment = 1, ;
Column2.ControlSource = "crsStornoDetalii.original", ;
Column2.FontSize = 10, ;
Column2.Name = "cOriginal", ;
Column2.ReadOnly = .T., ;
Column2.Width = 200, ;
Column3.Alignment = 1, ;
Column3.ControlSource = "crsStornoDetalii.nou", ;
Column3.FontSize = 10, ;
Column3.Name = "cNou", ;
Column3.ReadOnly = .T., ;
Column3.Width = 200
*< END OBJECT: ClassLib="_grd_base.vcx" BaseClass="grid" />
ADD OBJECT 'grdModificari.cElement.Header1' AS header WITH ;
Caption = "Element", ;
FontSize = 10, ;
Name = "Header1"
*< END OBJECT: BaseClass="header" />
ADD OBJECT 'grdModificari.cElement.Text1' AS textbox WITH ;
BackColor = 255,255,255, ;
BorderStyle = 0, ;
FontSize = 10, ;
ForeColor = 0,0,0, ;
Margin = 0, ;
Name = "Text1", ;
ReadOnly = .T., ;
SelectedBackColor = 100,185,255
*< END OBJECT: BaseClass="textbox" />
ADD OBJECT 'grdModificari.cNou.Header1' AS header WITH ;
Alignment = 1, ;
Caption = "Storno", ;
FontSize = 10, ;
Name = "Header1"
*< END OBJECT: BaseClass="header" />
ADD OBJECT 'grdModificari.cNou.Text1' AS textbox WITH ;
Alignment = 1, ;
BackColor = 255,255,255, ;
BorderStyle = 0, ;
FontSize = 10, ;
ForeColor = 0,0,0, ;
Margin = 0, ;
Name = "Text1", ;
ReadOnly = .T., ;
SelectedBackColor = 100,185,255
*< END OBJECT: BaseClass="textbox" />
ADD OBJECT 'grdModificari.cOriginal.Header1' AS header WITH ;
Alignment = 1, ;
Caption = "Original", ;
FontSize = 10, ;
Name = "Header1"
*< END OBJECT: BaseClass="header" />
ADD OBJECT 'grdModificari.cOriginal.Text1' AS textbox WITH ;
Alignment = 1, ;
BackColor = 255,255,255, ;
BorderStyle = 0, ;
FontSize = 10, ;
ForeColor = 0,0,0, ;
Margin = 0, ;
Name = "Text1", ;
ReadOnly = .T., ;
SelectedBackColor = 100,185,255
*< END OBJECT: BaseClass="textbox" />
ADD OBJECT 'lblAntet' AS _label WITH ;
AutoSize = .F., ;
Caption = "", ;
Height = 46, ;
Left = 12, ;
Name = "lblAntet", ;
Top = 38, ;
Width = 736, ;
WordWrap = .T.
*< END OBJECT: ClassLib="_baza.vcx" BaseClass="label" />
ADD OBJECT 'lblAvertisment' AS _label WITH ;
AutoSize = .F., ;
Caption = "", ;
ForeColor = 192,0,0, ;
Height = 40, ;
Left = 12, ;
Name = "lblAvertisment", ;
Top = 118, ;
Width = 736, ;
WordWrap = .T.
*< END OBJECT: ClassLib="_baza.vcx" BaseClass="label" />
ADD OBJECT 'lblMotiv' AS _label WITH ;
Caption = "Motiv (optional):", ;
Left = 270, ;
Name = "lblMotiv", ;
Top = 90
*< END OBJECT: ClassLib="_baza.vcx" BaseClass="label" />
ADD OBJECT 'lblNumar' AS _label WITH ;
Caption = "Numar storno:", ;
Left = 12, ;
Name = "lblNumar", ;
Top = 90
*< END OBJECT: ClassLib="_baza.vcx" BaseClass="label" />
ADD OBJECT 'lblStare' AS _label WITH ;
Caption = "Nevalidat", ;
FontBold = .T., ;
Left = 12, ;
Name = "lblStare", ;
Top = 452, ;
Width = 200
*< END OBJECT: ClassLib="_baza.vcx" BaseClass="label" />
ADD OBJECT 'txtMotiv' AS _textbox WITH ;
ControlSource = "Thisform.pcmotiv", ;
Height = 23, ;
Left = 375, ;
MaxLength = 200, ;
Name = "txtMotiv", ;
TabIndex = 2, ;
Top = 87, ;
Width = 373
*< END OBJECT: ClassLib="_baza.vcx" BaseClass="textbox" />
ADD OBJECT 'txtNumar' AS _textbox WITH ;
ControlSource = "Thisform.pcnumarnou", ;
Height = 23, ;
Left = 105, ;
Name = "txtNumar", ;
TabIndex = 1, ;
Top = 87, ;
Width = 150
*< END OBJECT: ClassLib="_baza.vcx" BaseClass="textbox" />
PROCEDURE Activate
DODEFAULT()
TRY
This.cmdValideaza.SetFocus()
CATCH
ENDTRY
ENDPROC
PROCEDURE EnableButoane
LPARAMETERS tlEnable
This.cmdValideaza.Enabled = m.tlEnable
This.cmdPdf.Enabled = m.tlEnable
This.cmdTrimite.Enabled = m.tlEnable
This.txtNumar.Enabled = m.tlEnable
This.txtMotiv.Enabled = m.tlEnable
_screen.MousePointer = IIF(m.tlEnable, 0, 11)
ENDPROC
PROCEDURE Init
LPARAMETERS toAnaf, tnIdEfactura, tcXmlOriginal, tcNumarOriginal, tdDataOriginal, ;
tcClient, tcCifFirma, tnTotal, tcMoneda, tlTest, tlB2C, tcFisierStorno, tcNumarNou, ;
tcAvertizare
This.poAnaf = toAnaf
This.pnIdEfactura = tnIdEfactura
This.pcXmlOriginal = tcXmlOriginal
This.pcNumarOriginal = tcNumarOriginal
This.pdDataOriginal = tdDataOriginal
This.pcClient = tcClient
This.pcCifFirma = tcCifFirma
This.pnTotal = tnTotal
This.pcMoneda = tcMoneda
This.plTest = tlTest
This.plB2C = tlB2C
This.pcFisierStorno = tcFisierStorno
This.pcNumarNou = tcNumarNou
This.Caption = 'Stornare eFactura ' + m.tcNumarOriginal + ' - ' + IIF(m.tlTest, 'TEST', 'PRODUCTIE')
This.Lb_titlu_alb_b121.Caption = This.Caption
This.lblAntet.Caption = 'Factura ' + m.tcNumarOriginal + ' din ' + Dtoc(m.tdDataOriginal) + ' -> Storno' + Chr(13) + Chr(10) + ;
'Client: ' + m.tcClient + ' CIF firma: ' + m.tcCifFirma + ' Moneda: ' + m.tcMoneda
This.lblAvertisment.Caption = m.tcAvertizare
This.grdModificari.SetAll("DynamicFontBold", "tip = 2", "Column")
This.SetStare(0)
ENDPROC
PROCEDURE RegenereazaXml
LOCAL lcNumarNou, lcMotiv, lcNotaFinal, laModif[1], lcMesaj, llSucces
lcNumarNou = ALLTRIM(This.pcNumarNou)
lcMotiv = ALLTRIM(This.pcMotiv)
IF Empty(m.lcNumarNou)
AMESSAGEBOX('Numarul stornului nu poate fi gol.', 0+48, _screen.Caption)
RETURN .F.
ENDIF
lcNotaFinal = 'Stornare factura ' + This.pcNumarOriginal + ' din data ' + Dtoc(This.pdDataOriginal) + IIF(Empty(m.lcMotiv), '', '. ' + m.lcMotiv)
llSucces = StornoEFacturaXML(This.pcXmlOriginal, m.lcNumarNou, m.lcNotaFinal, This.pcFisierStorno, @laModif, @lcMesaj)
IF !m.llSucces
This.edtStatus.Value = 'Eroare la regenerare: ' + m.lcMesaj
This.SetStare(0)
RETURN .F.
ENDIF
This.UmpleGrid(@laModif)
RETURN .T.
ENDPROC
PROCEDURE SetStare
LPARAMETERS tnStare
This.pnStare = m.tnStare
This.cmdPdf.Enabled = .T.
DO CASE
CASE m.tnStare = 2
* trimis - stare terminala (D26/D35)
This.lblStare.Caption = 'Trimis'
This.cmdValideaza.Enabled = .F.
This.cmdTrimite.Enabled = .F.
This.txtNumar.Enabled = .F.
This.txtMotiv.Enabled = .F.
CASE m.tnStare = 1
This.lblStare.Caption = 'Valid'
This.cmdValideaza.Enabled = .T.
This.cmdTrimite.Enabled = .T.
This.txtNumar.Enabled = .T.
This.txtMotiv.Enabled = .T.
OTHERWISE
This.lblStare.Caption = 'Nevalidat'
This.cmdValideaza.Enabled = .T.
This.cmdTrimite.Enabled = .F.
This.txtNumar.Enabled = .T.
This.txtMotiv.Enabled = .T.
ENDCASE
ENDPROC
PROCEDURE UmpleGrid
LPARAMETERS taModificari
LOCAL lnI, lcSelect
lcSelect = Select()
SELECT crsStornoDetalii
ZAP
FOR lnI = 1 TO Alen(taModificari, 1)
INSERT INTO crsStornoDetalii (element, original, nou, tip) VALUES ;
(taModificari[m.lnI,1], Transform(taModificari[m.lnI,2]), Transform(taModificari[m.lnI,3]), taModificari[m.lnI,4])
ENDFOR
SELECT (m.lcSelect)
This.grdModificari.Refresh()
ENDPROC
PROCEDURE cmdPdf.Click
LOCAL lcXmlCurent, lcFisierPdf
lcXmlCurent = Filetostr(Thisform.pcFisierStorno)
lcFisierPdf = GetPdfPath('EFACTURA','TRIMISE') + 'storno_' + WindowsSpecialCharacters(ALLTRIM(Thisform.pcNumarNou)) + '.pdf'
lcFisierPdf = Thisform.poAnaf.Xml2PdfAnaf(m.lcXmlCurent, m.lcFisierPdf, .T.)
IF !Empty(m.lcFisierPdf) AND File(m.lcFisierPdf)
open_default_app(m.lcFisierPdf)
ENDIF
ENDPROC
PROCEDURE cmdTrimite.Click
LOCAL lcMesajConf, loReturn, lcDirProcesat, lcFisierProcesat
IF Thisform.pnStare <> 1
RETURN
ENDIF
lcMesajConf = 'Trimiteti la ANAF (' + IIF(Thisform.plTest,'TEST','PRODUCTIE') + ') stornarea facturii ' + Thisform.pcNumarOriginal + ;
' ca ' + ALLTRIM(Thisform.pcNumarNou) + ', total ' + Transform(Thisform.pnTotal) + ' ' + Thisform.pcMoneda + '? Operatia nu se poate anula.'
IF AMESSAGEBOX(m.lcMesajConf, 4+32+256, _screen.Caption) <> 6
RETURN
ENDIF
Thisform.EnableButoane(.F.)
WAIT WINDOW 'Se trimite...' NOWAIT
Thisform.poAnaf.RefreshTokenAuto()
loReturn = Thisform.poAnaf.TrimiteFacturaXML(Thisform.pcFisierStorno, .F., .F., .F., .F., Thisform.plB2C)
CLEAR TYPEAHEAD
WAIT CLEAR
IF !Empty(loReturn.cIdIncarcare)
lcDirProcesat = GetPdfPath('EFACTURA','TRIMISE') + 'procesat\'
lcFisierProcesat = loReturn.cIdIncarcare + '_' + Justfname(Thisform.pcFisierStorno)
IF Thisform.poAnaf.SaveFile(Thisform.pcFisierStorno, .F., m.lcDirProcesat, m.lcFisierProcesat)
TRY
Delete File (Thisform.pcFisierStorno)
CATCH
ENDTRY
ENDIF
goLog.Log('Storno eFactura: ' + Thisform.pcNumarOriginal + ' -> ' + ALLTRIM(Thisform.pcNumarNou) + ', index ' + loReturn.cIdIncarcare + ', ' + IIF(Thisform.plTest,'TEST','PRODUCTIE'))
Thisform.edtStatus.Value = 'Trimis. Index incarcare ' + loReturn.cIdIncarcare + ' (' + IIF(Thisform.plTest,'TEST','PRODUCTIE') + '). ' + ;
'Stergeti factura din ROA si emiteti factura corecta cu acelasi numar. Storno-ul apare in Trimise dupa descarcarea mesajelor.'
Thisform.EnableButoane(.T.)
Thisform.SetStare(2)
ELSE
Thisform.edtStatus.Value = 'Eroare: ' + loReturn.cMesaj + '. Verificati in Trimise, dupa descarcare, inainte de a retrimite.'
Thisform.EnableButoane(.T.)
Thisform.SetStare(0)
ENDIF
ENDPROC
PROCEDURE cmdValideaza.Click
LOCAL llValid, lcMesajValid, loSrv, loExport
IF !Thisform.RegenereazaXml()
RETURN
ENDIF
loSrv = Thisform.poAnaf
Thisform.EnableButoane(.F.)
WAIT WINDOW 'Se valideaza...' NOWAIT
loSrv.RefreshTokenAuto()
loExport = CREATEOBJECT('ExportEFactura', .T., loSrv.cToken)
llValid = m.loExport.ValidareEFacturaANAF(Thisform.pcFisierStorno, @lcMesajValid)
CLEAR TYPEAHEAD
WAIT CLEAR
Thisform.EnableButoane(.T.)
IF m.llValid
Thisform.edtStatus.Value = 'Valid.'
Thisform.SetStare(1)
ELSE
Thisform.edtStatus.Value = IIF(Empty(m.lcMesajValid), 'Validare indisponibila - verificati tokenul/reteaua.', 'Invalid: ' + m.lcMesajValid)
Thisform.SetStare(0)
ENDIF
ENDPROC
PROCEDURE txtMotiv.InteractiveChange
IF Thisform.pnStare <> 0
Thisform.SetStare(0)
ENDIF
ENDPROC
PROCEDURE txtNumar.InteractiveChange
IF Thisform.pnStare <> 0
Thisform.SetStare(0)
ENDIF
ENDPROC
ENDDEFINE

View File

@@ -132,6 +132,8 @@ Cand un bug apare "de azi", compara cu clasa veche fara sa atingi SVN:
`INKEY(0)` asteapta, timerele VFP nu se declanseaza, deci un dialog/o bucla blocata asa nu poate
fi deblocata din interiorul procesului - in rulare headless arata ca un proces viu care nu mai
scrie in log.
- **Doua constructii care NU compileaza, dar trec de `txt2vcx`** (storno eFactura, 18.09.2026): `NEWOBJECT('c', 'lib.vcx', , p1, ...)` cu argumentul 3 omis ("Function argument value, type, or count is invalid" - pune `''`) si `CREATEOBJECT(...).Metoda()` inlantuit ("Syntax error"). `txt2vcx` (si `-DryRun`) NU compileaza metodele: dupa write-back, copiaza `.vcx`/`.vct` in ACELASI folder sub alt nume (parintii se rezolva relativ), `COMPILE CLASSLIB` headless, citeste `.ERR`, sterge copia.
- **`FOR EACH` pe `loDom.selectNodes(...)`** da la rulare "Expression evaluator failed". Foloseste `loN = loDom.selectNodes(...)` + `FOR i = 0 TO loN.length - 1` / `loN.item(i)`.
## 7. Capcane la SCRIEREA scriptului de test
@@ -178,3 +180,14 @@ Cand un bug apare "de azi", compara cu clasa veche fara sa atingi SVN:
si apoi apeleaza cod care citeste globalul masoara valoarea veche, fara niciun semn de eroare.
Verifica intai pe ce cale citeste codul testat; daca e globalul, seteaza-l direct in test (si
restaureaza-l la final), nu prin `scrie_optiune()`.
- **Liniile de dinainte de `ON ERROR` dau dialog nativ pe ecranul utilizatorului.** `ALLTRIM(NVL(tcParam, ''))` pe un
parametru nepasat (`.F.`) = "Function argument value..." ca dialog "Program Error", nu in log. Seteaza `ON ERROR` pe
primele linii si foloseste `TRANSFORM(tcParam)` pentru parametri optionali.
- **Eroarea SQL prin `goExecutor` e o forma modala INTERNA VFP**: `watchdog_vfp.ps1` nu o vede (nu e fereastra top-level),
procesul sta pana la timeout. Verifica fiecare `SELECT` din test cu `sqlplus` inainte de rulare (ex. `anaf_efactura`
nu are `numar_act`/`data_act` - sunt in view-urile `anaf_vefactura_*`).
- **Forma din `.vcx` cu `CREATEOBJECT` direct poate pica in `Init` cu erori de binding** ("Error with _checkbox1 - Value :
Data type mismatch", CREATEOBJECT intoarce `.F.`), desi in aplicatie merge. Fa un stub `.scx` bazat pe clasa si deschide-l
cu `DO FORM ... NAME lo NOSHOW` - asa se instantiaza ca in aplicatie, iar erorile ar arata calea completa a controlului.
- **Rularile de test se fac DOAR prin `watchdog_vfp.ps1 -AutoDismiss`**: un lansator propriu fara plasa a lasat 3 dialoguri
modale pe ecranul utilizatorului (18.09.2026).

View File

@@ -802,8 +802,9 @@ select a.*, cast(NULL as varchar2(1000)) as factura_detalii, 0 as modificat
* parseaza un director cu xml efactura si le trimite la API ANAF
********************************
Procedure TrimiteFacturaXML
LPARAMETERS tcFile, tlValidare, tlValidareOnline, tlNuTrimiteEFacturaANAF, tlExtern
LPARAMETERS tcFile, tlValidare, tlValidareOnline, tlNuTrimiteEFacturaANAF, tlExtern, tlB2C
* tlB2C: (optional) forteaza uploadb2c la trimitere, vezi SendEfactura
Local loXmlEFactura As 'ExportEFactura'
Local lcErrorMessage, lcExecutionStatus, lcFile, lcIndexIncarcare, lcMesaj, llSucces, llValid, loEx
Local loXML, lcMesajValidare, llExtern, lcToken
@@ -833,7 +834,7 @@ select a.*, cast(NULL as varchar2(1000)) as factura_detalii, 0 as modificat
ENDIF
IF m.llValid AND !m.tlNuTrimiteEFacturaANAF
loReturn = This.SendEfactura(m.lcFile, m.llExtern)
loReturn = This.SendEfactura(m.lcFile, m.llExtern, .F., m.tlB2C)
llSucces = loReturn.lSucces
AddProperty(loReturn, "cIdIncarcare", '')
AddProperty(loReturn, "cMesaj", '')
@@ -915,15 +916,16 @@ select a.*, cast(NULL as varchar2(1000)) as factura_detalii, 0 as modificat
* Genereaza xml eFactura, valideaza xml si trimite la ANAF
Function SendEfactura
Lparameters tcFile, tlExtern, tlMesaj
Lparameters tcFile, tlExtern, tlMesaj, tlB2C
* tcFile: calea catre fisierul xml efactura / sau xml mesaj
* tlExtern: daca se apeleaza cu parametrul Extern=DA (pentru clientii externi)
* tlMesaj: daca mesajul este o factura sau un mesaj catre furnizor
* tlB2C: (optional) forteaza uploadb2c, pentru xml-uri cu prefixe straine unde detectia CIF nu se aplica
Local loEx As Exception
Local loHTTP As 'winHTTP.winHTTPrequest.5.1'
Local loReturn As "empty"
Local lcCodFiscal, lcExtern, lcFile, lcSend, lcServer, lcToken, llOk, llTest, llB2C
Local lcCodFiscal, lcExtern, lcFile, lcSend, lcServer, lcStandard, lcToken, llOk, llTest, llB2C
LOCAL lcCustomer, lcPartyLegal, lcCodFiscalClient
loReturn = Createobject("empty")
@@ -948,15 +950,16 @@ select a.*, cast(NULL as varchar2(1000)) as factura_detalii, 0 as modificat
* Verific daca clientul este persoana fizica si folosesc upload2bc in loc de upload
lcCustomer = STREXTRACT(lcSend, '<cac:AccountingCustomerParty>', '</cac:AccountingCustomerParty>',1,1)
lcPartyLegal = STREXTRACT(lcCustomer, '<cac:PartyLegalEntity>', '</cac:PartyLegalEntity>',1,1)
lcCodFiscalClient = ALLTRIM(STREXTRACT(m.lcPartyLegal, '<cbc:CompanyID>','</cbc:CompanyID>',1,1))
llB2C = (LEN(m.lcCodFiscalClient) = 13) && persoane fizice
TRY
lcCodFiscalClient = ALLTRIM(STREXTRACT(m.lcPartyLegal, '<cbc:CompanyID>','</cbc:CompanyID>',1,1))
llB2C = (LEN(m.lcCodFiscalClient) = 13) OR m.tlB2C && persoane fizice
lcStandard = IIF('CreditNote'$m.lcSend, 'CN', 'UBL')
TRY
IF !m.tlMesaj
lcServer = [https://api.anaf.ro/] + Iif(m.llTest, [test], [prod]) + [/FCTEL/rest/upload] + IIF(m.llB2C,'b2c','') + [?standard=UBL&cif=] + m.lcCodFiscal + m.lcExtern
lcServer = [https://api.anaf.ro/] + Iif(m.llTest, [test], [prod]) + [/FCTEL/rest/upload] + IIF(m.llB2C,'b2c','') + [?standard=] + m.lcStandard + [&cif=] + m.lcCodFiscal + m.lcExtern
ELSE
lcServer = [https://api.anaf.ro/] + Iif(m.llTest, [test], [prod]) + [/FCTEL/rest/upload] + IIF(m.llB2C,'b2c','') + [?standard=RASP&cif=] + m.lcCodFiscal
ENDIF
ENDIF
loHTTP = Createobject('winHTTP.winHTTPrequest.5.1')
loHTTP.Open('POST', lcServer, .F.)
@@ -4069,6 +4072,432 @@ Procedure ParseEFactura
Return loReturn
Endproc && ParseEFactura
****************************************
* Extrage xml-ul original descarcat de la ANAF pentru un rand ANAF_EFACTURA: dezarhiveaza
* detalii_zip (BLOB din baza), cu rezerva fisierul zip de pe disc din filepath.
* tnId: ANAF_EFACTURA.ID
* tcXml: OUT continutul xml (octetii bruti din arhiva ANAF)
* tcMesaj: OUT mesaj de eroare daca RETURN = .F.
****************************************
FUNCTION GetXmlOriginalEFactura
LPARAMETERS tnId, tcXml, tcMesaj
LOCAL lcSelect, lcFiltru, lcSchema, lcOrder, llAfiseaza, lcGroup, llModParam, lcFiltruOriginal
LOCAL lcIdIncarcare, lcFilePath, lcZip, lcDir, lcXmlFile, lcSemnatura, llSucces, llResult
PRIVATE poXmlOriginalZip
tcXml = ''
tcMesaj = ''
lcIdIncarcare = ''
lcFilePath = ''
lcZip = ''
llResult = .F.
poXmlOriginalZip = Null
IF Vartype(m.tnId) <> 'N' OR m.tnId <= 0
tcMesaj = 'Id factura invalid.'
RETURN .F.
ENDIF
* RETURN e interzis in TRY/CATCH (eroare 4165) - iesire unica prin EXIT, RETURN dupa ENDTRY
TRY
DO WHILE .T.
lcSchema = [id_incarcare V(36), lc_filepath V(250), fisier W]
lcSelect = [select id_incarcare, filepath, detalii_zip from anaf_efactura where id = ] + Transform(m.tnId)
lcFiltru = ''
lcOrder = ''
llAfiseaza = .F.
lcGroup = ''
llModParam = .T.
lcFiltruOriginal = ''
Gencursor('poXmlOriginalZip', 'cXmlOriginalTemp', m.lcSelect, m.lcFiltru, m.lcSchema, m.lcOrder, m.llAfiseaza, m.lcGroup, m.llModParam, m.lcFiltruOriginal)
poXmlOriginalZip.ca_baza1.afisare()
IF Used('cXmlOriginalTemp') AND Reccount('cXmlOriginalTemp') = 1
SELECT cXmlOriginalTemp
lcIdIncarcare = Alltrim(Nvl(id_incarcare, ''))
lcFilePath = Alltrim(Nvl(lc_filepath, ''))
IF !Empty(Nvl(fisier, ''))
lcZip = Addbs(Sys(2023)) + 'storno_' + Transform(m.tnId) + '_' + Sys(2015) + '.zip'
Strtofile(fisier, m.lcZip)
ENDIF
USE IN (SELECT('cXmlOriginalTemp'))
ENDIF
IF Empty(m.lcZip) AND !Empty(m.lcFilePath) AND File(m.lcFilePath)
lcZip = m.lcFilePath
ENDIF
IF Empty(m.lcZip) OR !File(m.lcZip)
tcMesaj = 'Factura nu are XML descarcat.'
EXIT
ENDIF
IF Empty(m.lcIdIncarcare)
tcMesaj = 'Factura nu are id_incarcare.'
EXIT
ENDIF
lcDir = ''
llSucces = MyUnzip(m.lcZip, @lcDir, @tcMesaj, .T.)
IF m.lcZip <> m.lcFilePath
Delete File (m.lcZip)
ENDIF
IF !m.llSucces
EXIT
ENDIF
lcXmlFile = Addbs(m.lcDir) + m.lcIdIncarcare + '.xml'
lcSemnatura = Addbs(m.lcDir) + 'semnatura_' + m.lcIdIncarcare + '.xml'
IF !File(m.lcXmlFile)
tcMesaj = 'Arhiva nu contine fisierul ' + m.lcIdIncarcare + '.xml.'
EXIT
ENDIF
tcXml = Filetostr(m.lcXmlFile)
Delete File (m.lcXmlFile)
IF File(m.lcSemnatura)
Delete File (m.lcSemnatura)
ENDIF
llResult = .T.
EXIT
ENDDO
CATCH TO loEx
tcMesaj = 'GetXmlOriginalEFactura eroare: ' + loEx.Message
ENDTRY
RETURN m.llResult
ENDFUNC && GetXmlOriginalEFactura
****************************************
* Insereaza loNewNode inaintea lui loRefNode, sau la sfarsit daca loRefNode nu exista
* (insertBefore cu referinta null nu se poate apela sigur din VFP)
****************************************
FUNCTION InsertBeforeSafe
LPARAMETERS loParent, loNewNode, loRefNode
IF Isnull(m.loRefNode)
RETURN loParent.appendChild(m.loNewNode)
ENDIF
RETURN loParent.insertBefore(m.loNewNode, m.loRefNode)
ENDFUNC && InsertBeforeSafe
****************************************
* Neaga (schimba semnul) unui sir numeric din xml, pastrand zecimalele exact ca in original.
* tcValoare: valoarea trim-uita din xml
* tlOk: OUT .F. daca tcValoare nu e un numar valid (^[+-]?(\d+(\.\d*)?|\.\d+)$)
* Zero (orice forma, inclusiv -0.00) ramane neschimbat.
****************************************
FUNCTION NegheazaValoareXml
LPARAMETERS tcValoare, tlOk
LOCAL lcMatch, lcNumeric
tlOk = .F.
lcMatch = GetRegExp(m.tcValoare, '^[+-]?(\d+(\.\d*)?|\.\d+)$')
IF Empty(m.lcMatch) OR m.lcMatch <> m.tcValoare
RETURN ''
ENDIF
tlOk = .T.
DO CASE
CASE Left(m.tcValoare, 1) = '-'
lcNumeric = Substr(m.tcValoare, 2)
RETURN Iif(Val(m.lcNumeric) = 0, m.tcValoare, m.lcNumeric)
CASE Left(m.tcValoare, 1) = '+'
lcNumeric = Substr(m.tcValoare, 2)
RETURN Iif(Val(m.lcNumeric) = 0, m.tcValoare, '-' + m.lcNumeric)
OTHERWISE
RETURN Iif(Val(m.tcValoare) = 0, m.tcValoare, '-' + m.tcValoare)
ENDCASE
ENDFUNC && NegheazaValoareXml
****************************************
* '' daca loNod nu e sub o linie (InvoiceLine/CreditNoteLine), altfel 'Linia <nr> - '
****************************************
FUNCTION LineiaEticheta
LPARAMETERS loNod
LOCAL loParinte, loIdLinie, lcNr
loParinte = loNod.parentNode
DO WHILE !Isnull(m.loParinte)
IF Inlist(loParinte.baseName, 'InvoiceLine', 'CreditNoteLine')
lcNr = ''
loIdLinie = loParinte.selectSingleNode("*[local-name()='ID']")
IF !Isnull(m.loIdLinie)
lcNr = Alltrim(loIdLinie.Text)
ENDIF
RETURN 'Linia ' + m.lcNr + ' - '
ENDIF
loParinte = loParinte.parentNode
ENDDO
RETURN ''
ENDFUNC && LineiaEticheta
****************************************
* Eticheta romana pentru gridul Element | Original | Storno al unui nod negat
****************************************
FUNCTION EtichetaNodXmlStorno
LPARAMETERS loNod
LOCAL lcBase, lcMoneda, lcLinie, loAttrCur, lcEticheta
lcBase = loNod.baseName
lcMoneda = ''
loAttrCur = loNod.Attributes.getNamedItem('currencyID')
IF !Isnull(m.loAttrCur)
lcMoneda = ' (' + loAttrCur.Text + ')'
ENDIF
lcLinie = LineiaEticheta(m.loNod)
DO CASE
CASE Inlist(m.lcBase, 'InvoicedQuantity', 'CreditedQuantity')
lcEticheta = 'Cantitate'
CASE m.lcBase = 'LineExtensionAmount'
lcEticheta = Iif(Empty(m.lcLinie), 'Valoare neta totala', 'Valoare linie')
CASE m.lcBase = 'TaxAmount'
lcEticheta = 'TVA'
CASE m.lcBase = 'TaxableAmount'
lcEticheta = 'Baza TVA'
CASE m.lcBase = 'Amount'
lcEticheta = 'Reducere/majorare'
CASE m.lcBase = 'BaseAmount'
lcEticheta = 'Baza reducere/majorare'
CASE m.lcBase = 'TaxExclusiveAmount'
lcEticheta = 'Total fara TVA'
CASE m.lcBase = 'TaxInclusiveAmount'
lcEticheta = 'Total cu TVA'
CASE m.lcBase = 'AllowanceTotalAmount'
lcEticheta = 'Total reduceri'
CASE m.lcBase = 'ChargeTotalAmount'
lcEticheta = 'Total taxe suplimentare'
CASE m.lcBase = 'PrepaidAmount'
lcEticheta = 'Avans platit'
CASE m.lcBase = 'PayableRoundingAmount'
lcEticheta = 'Rotunjire'
CASE m.lcBase = 'PayableAmount'
lcEticheta = 'Total de plata'
OTHERWISE
lcEticheta = m.lcBase
ENDCASE
RETURN m.lcLinie + m.lcEticheta + m.lcMoneda
ENDFUNC && EtichetaNodXmlStorno
****************************************
* Genereaza xml-ul de stornare al unei facturi eFactura deja trimise la ANAF: neaga
* sumele/cantitatile, schimba numarul, adauga BillingReference catre factura originala si o Nota.
* tcXml: continutul original al xml-ului (FILETOSTR pe fisierul din arhiva ANAF, GetXmlOriginalEFactura)
* tcNumarNou: numarul noii facturi (gol = numarul original + '-S')
* tcNota: text Nota adaugat pe document (gol = nu se adauga)
* tcFisier: calea unde se salveaza xml-ul storno
* taModificari: OUT array [n,4] (eticheta romana, original, nou, tip: 1 antet/2 total/3 linie)
* tcMesaj: OUT mesaj de eroare daca RETURN = .F. (numeste nodul/motivul blocarii)
****************************************
FUNCTION StornoEFacturaXML
LPARAMETERS tcXml, tcNumarNou, tcNota, tcFisier, taModificari, tcMesaj
LOCAL loDOM, loRoot, loNodID, loNodSupplier, loNodIssueDate, loNod, loNoduri
LOCAL lcPrefixCbc, lcUriCbc, lcPrefixCac, lcUriCac, lcNumarVechi, lcDataOriginala
LOCAL lcXPath, lcTemp, lcValoare, lcValoareNoua, lcLinieEticheta, lnI, lnModif, llOk, llValoareInvalida, llResult
LOCAL loBillingRef, loInvDocRef, loIdRef, loDateRef, loChild, loInsertBefore
LOCAL loNoteNode, loLastNote, loTypeCode
Dimension taModificari[1, 4]
taModificari[1, 1] = ''
tcMesaj = ''
lnModif = 0
llResult = .F.
IF Vartype(m.tcNumarNou) <> 'C'
tcNumarNou = ''
ENDIF
IF Vartype(m.tcNota) <> 'C'
tcNota = ''
ENDIF
IF Empty(m.tcXml)
tcMesaj = 'XML original gol.'
RETURN .F.
ENDIF
lcTemp = Addbs(Sys(2023)) + 'storno_src_' + Sys(2015) + '.xml'
* RETURN e interzis in TRY/CATCH (eroare 4165) - iesire unica prin EXIT, RETURN dupa ENDTRY
TRY
DO WHILE .T.
Strtofile(m.tcXml, m.lcTemp)
loDOM = Createobject('MSXML2.DOMDocument.6.0')
loDOM.Async = .F.
loDOM.preserveWhiteSpace = .T.
loDOM.setProperty('SelectionLanguage', 'XPath')
loDOM.Load(m.lcTemp)
IF loDOM.parseError.errorcode <> 0
tcMesaj = 'Eroare parsare XML: ' + loDOM.parseError.reason
EXIT
ENDIF
loRoot = loDOM.documentElement
IF Isnull(m.loRoot) OR !Inlist(loRoot.baseName, 'Invoice', 'CreditNote')
tcMesaj = 'Documentul nu e Invoice/CreditNote.'
EXIT
ENDIF
loNodID = loRoot.selectSingleNode("*[local-name()='ID']")
loNodSupplier = loRoot.selectSingleNode("*[local-name()='AccountingSupplierParty']")
loNodIssueDate = loRoot.selectSingleNode("*[local-name()='IssueDate']")
IF Isnull(m.loNodID) OR Isnull(m.loNodSupplier) OR Isnull(m.loNodIssueDate)
tcMesaj = 'Lipseste ID, IssueDate sau AccountingSupplierParty pe radacina.'
EXIT
ENDIF
lcPrefixCbc = loNodID.Prefix
lcUriCbc = loNodID.namespaceURI
lcPrefixCac = loNodSupplier.Prefix
lcUriCac = loNodSupplier.namespaceURI
lcNumarVechi = Alltrim(loNodID.Text)
lcDataOriginala = Alltrim(loNodIssueDate.Text)
IF Empty(m.tcNumarNou)
tcNumarNou = m.lcNumarVechi + '-S'
ENDIF
IF Empty(m.tcNumarNou)
tcMesaj = 'Numar factura gol.'
EXIT
ENDIF
taModificari[1, 1] = 'Numar factura'
taModificari[1, 2] = m.lcNumarVechi
taModificari[1, 3] = m.tcNumarNou
taModificari[1, 4] = 1
lnModif = 1
loNodID.Text = m.tcNumarNou
* BillingReference catre factura originala, inaintea primului nod din multimea UBL cunoscuta
loInvDocRef = loDOM.createNode(1, Iif(Empty(m.lcPrefixCac), 'InvoiceDocumentReference', m.lcPrefixCac + ':InvoiceDocumentReference'), m.lcUriCac)
loIdRef = loDOM.createNode(1, Iif(Empty(m.lcPrefixCbc), 'ID', m.lcPrefixCbc + ':ID'), m.lcUriCbc)
loIdRef.Text = m.lcNumarVechi
loInvDocRef.appendChild(m.loIdRef)
loDateRef = loDOM.createNode(1, Iif(Empty(m.lcPrefixCbc), 'IssueDate', m.lcPrefixCbc + ':IssueDate'), m.lcUriCbc)
loDateRef.Text = m.lcDataOriginala
loInvDocRef.appendChild(m.loDateRef)
loBillingRef = loDOM.createNode(1, Iif(Empty(m.lcPrefixCac), 'BillingReference', m.lcPrefixCac + ':BillingReference'), m.lcUriCac)
loBillingRef.appendChild(m.loInvDocRef)
loInsertBefore = Null
loChild = loRoot.firstChild
DO WHILE !Isnull(m.loChild)
IF Inlist(loChild.baseName, 'DespatchDocumentReference', 'ReceiptDocumentReference', 'StatementDocumentReference', ;
'OriginatorDocumentReference', 'ContractDocumentReference', 'AdditionalDocumentReference', ;
'ProjectReference', 'Signature', 'AccountingSupplierParty')
loInsertBefore = m.loChild
EXIT
ENDIF
loChild = loChild.nextSibling
ENDDO
IF Isnull(m.loInsertBefore)
loInsertBefore = m.loNodSupplier
ENDIF
InsertBeforeSafe(m.loRoot, m.loBillingRef, m.loInsertBefore)
lnModif = m.lnModif + 1
Dimension taModificari[m.lnModif, 4]
taModificari[m.lnModif, 1] = 'Referinta (factura originala)'
taModificari[m.lnModif, 2] = ''
taModificari[m.lnModif, 3] = m.lcNumarVechi + ' / ' + m.lcDataOriginala
taModificari[m.lnModif, 4] = 1
* Nota, dupa ultima Note existenta sau dupa TypeCode-ul radacinii
IF !Empty(m.tcNota)
loNoteNode = loDOM.createNode(1, Iif(Empty(m.lcPrefixCbc), 'Note', m.lcPrefixCbc + ':Note'), m.lcUriCbc)
loNoteNode.Text = m.tcNota
loLastNote = Null
loChild = loRoot.firstChild
DO WHILE !Isnull(m.loChild)
IF loChild.baseName = 'Note'
loLastNote = m.loChild
ENDIF
loChild = loChild.nextSibling
ENDDO
IF !Isnull(m.loLastNote)
InsertBeforeSafe(m.loRoot, m.loNoteNode, m.loLastNote.nextSibling)
ELSE
loTypeCode = loRoot.selectSingleNode("*[local-name()='InvoiceTypeCode' or local-name()='CreditNoteTypeCode']")
IF !Isnull(m.loTypeCode)
InsertBeforeSafe(m.loRoot, m.loNoteNode, m.loTypeCode.nextSibling)
ELSE
InsertBeforeSafe(m.loRoot, m.loNoteNode, m.loRoot.firstChild)
ENDIF
ENDIF
lnModif = m.lnModif + 1
Dimension taModificari[m.lnModif, 4]
taModificari[m.lnModif, 1] = 'Nota'
taModificari[m.lnModif, 2] = ''
taModificari[m.lnModif, 3] = m.tcNota
taModificari[m.lnModif, 4] = 1
ENDIF
* negare sume/cantitati: o singura interogare, fiecare nod o singura data, in ordinea din document
lcXPath = "//*[(local-name()='InvoicedQuantity' or local-name()='CreditedQuantity'" + ;
" or local-name()='LineExtensionAmount' or local-name()='TaxAmount' or local-name()='TaxableAmount'" + ;
" or local-name()='TaxExclusiveAmount' or local-name()='TaxInclusiveAmount' or local-name()='AllowanceTotalAmount'" + ;
" or local-name()='ChargeTotalAmount' or local-name()='PrepaidAmount' or local-name()='PayableRoundingAmount'" + ;
" or local-name()='PayableAmount'" + ;
" or ((local-name()='Amount' or local-name()='BaseAmount') and local-name(parent::*)='AllowanceCharge'))" + ;
" and not(ancestor::*[local-name()='Price'])]"
loNoduri = loRoot.selectNodes(m.lcXPath)
llValoareInvalida = .F.
FOR lnI = 0 TO loNoduri.length - 1
loNod = loNoduri.Item(m.lnI)
lcValoare = Alltrim(loNod.Text)
lcValoareNoua = NegheazaValoareXml(m.lcValoare, @llOk)
IF !m.llOk
tcMesaj = 'Valoare nenumerica in ' + loNod.baseName + ': "' + m.lcValoare + '".'
llValoareInvalida = .T.
EXIT
ENDIF
IF m.lcValoareNoua <> m.lcValoare
lcLinieEticheta = LineiaEticheta(m.loNod)
lnModif = m.lnModif + 1
Dimension taModificari[m.lnModif, 4]
taModificari[m.lnModif, 1] = EtichetaNodXmlStorno(m.loNod)
taModificari[m.lnModif, 2] = m.lcValoare
taModificari[m.lnModif, 3] = m.lcValoareNoua
taModificari[m.lnModif, 4] = Iif(Empty(m.lcLinieEticheta), 2, 3)
loNod.Text = m.lcValoareNoua
ENDIF
ENDFOR
IF m.llValoareInvalida
EXIT
ENDIF
loDOM.save(m.tcFisier)
IF !File(m.tcFisier)
tcMesaj = 'Salvarea xml-ului storno a esuat: ' + m.tcFisier
EXIT
ENDIF
llResult = .T.
EXIT
ENDDO
CATCH TO loEx
tcMesaj = 'StornoEFacturaXML eroare: ' + loEx.Message
FINALLY
IF File(m.lcTemp)
Delete File (m.lcTemp)
ENDIF
ENDTRY
RETURN m.llResult
ENDFUNC && StornoEFacturaXML
******************************
PROCEDURE TestParseEFactura

View File

@@ -1240,7 +1240,7 @@ Define Class ExportEFactura As Custom
* lReturn: .T. valid, .F. invalid
Local loReturn, llValid
Local loHTTP As 'winHTTP.winHTTPrequest.5.1'
Local lcFileName, lcFisierRaspuns, lcMesaj, lcSend, lcServer, lcStare, llOk, llSilentios, llSucces
Local lcFileName, lcFisierRaspuns, lcMesaj, lcSend, lcServer, lcStare, lcTip, llOk, llSilentios, llSucces
Local lnMesaj, lnMesaje, loJson, loMessage, loEx as Exception, lcToken
llValid = .F.
@@ -1268,13 +1268,14 @@ Define Class ExportEFactura As Custom
If !Empty(m.lcFileName) And Type('lcFileName') = 'C' And File(m.lcFileName)
TRY
lcSend = Filetostr(m.lcFileName)
lcTip = IIF('CreditNote'$m.lcSend, 'FCN', 'FACT1')
IF !EMPTY(m.lcToken)
* validare oauth
lcServer = [https://api.anaf.ro/prod/FCTEL/rest/validare/FACT1]
lcServer = [https://api.anaf.ro/prod/FCTEL/rest/validare/] + m.lcTip
ELSE
* validare fara autentificare
lcServer = [https://webservicesp.anaf.ro/prod/FCTEL/rest/validare/FACT1]
lcServer = [https://webservicesp.anaf.ro/prod/FCTEL/rest/validare/] + m.lcTip
ENDIF
loHTTP = Createobject('winHTTP.winHTTPrequest.5.1')

View File

@@ -0,0 +1,19 @@
*--------------------------------------------------------------------------------------------------------------------------------------------------------
* (EN) AUTOGENERATED - ATTENTION!! - NOT INTENDED FOR EXECUTION!! USE ONLY FOR MERGING CHANGES AND STORING WITH SCM TOOLS!!
*--------------------------------------------------------------------------------------------------------------------------------------------------------
*< FOXBIN2PRG: Version="1.21" SourceFile="frmtest_anaf.scx" CPID="1252" /> (Solo para binarios VFP 9 / Only for VFP 9 binaries)
*
*
DEFINE CLASS dataenvironment AS dataenvironment
*< CLASSDATA: Baseclass="dataenvironment" Timestamp="" Scale="" Uniqueid="" ClassIcon="1" />
*<PropValue>
DataSource = .NULL.
Height = 0
Left = 0
Name = "Dataenvironment"
Top = 0
Width = 0
*</PropValue>
ENDDEFINE