Merge remote-tracking branch 'remotes/origin/master' into Improvements
This commit is contained in:
10
History.md
10
History.md
@@ -1,7 +1,17 @@
|
||||
2.0.3
|
||||
=======
|
||||
|
||||
|
||||
- #201 correct embedded files in XRechnung
|
||||
|
||||
2.0.2
|
||||
=======
|
||||
2020-11-25
|
||||
|
||||
- #197 file attachments
|
||||
- Charges/Allowances CategoryCode improvement PR #198 Thanks to weclapp-dev
|
||||
- support reverse charge
|
||||
- support intra community supply also in product class, not only interface
|
||||
|
||||
2.0.1
|
||||
=====
|
||||
|
||||
@@ -3,14 +3,14 @@
|
||||
<parent>
|
||||
<groupId>org.mustangproject</groupId>
|
||||
<artifactId>core</artifactId>
|
||||
<version>2.0.2-SNAPSHOT</version>
|
||||
<version>2.0.3-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>org.mustangproject</groupId>
|
||||
<artifactId>Mustang-CLI</artifactId>
|
||||
<name>Mustang commandline tool</name>
|
||||
<packaging>jar</packaging>
|
||||
<version>2.0.2-SNAPSHOT</version>
|
||||
<version>2.0.3-SNAPSHOT</version>
|
||||
<repositories>
|
||||
<repository>
|
||||
<!-- for jargs -->
|
||||
@@ -31,7 +31,7 @@
|
||||
<dependency>
|
||||
<groupId>org.mustangproject</groupId>
|
||||
<artifactId>validator</artifactId>
|
||||
<version>2.0.2-SNAPSHOT</version>
|
||||
<version>2.0.3-SNAPSHOT</version>
|
||||
<!-- prototypes of new mustangproject versions can be installed by referring to them and installed to the local repo from a jar file with
|
||||
mvn install:install-file -Dfile=mustang-1.5.4-SNAPSHOT.jar -DgroupId=org.mustangproject.ZUGFeRD -DartifactId=mustang -Dversion=1.5.4 -Dpackaging=jar -DgeneratePom=true
|
||||
-->
|
||||
|
||||
@@ -25,7 +25,7 @@ If you setup a Maven project, you can grab the artifacts using
|
||||
<dependency>
|
||||
<groupId>org.mustangproject</groupId>
|
||||
<artifactId>library</artifactId>
|
||||
<version>2.0.0</version>
|
||||
<version>2.0.2</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
|
||||
@@ -171,7 +171,6 @@ Change to the project directory and run
|
||||
* clean the release with `mvn release:clean` and prepare the release with
|
||||
* `mvn release:prepare -DignoreSnapshots=true` and enter the version numbers.
|
||||
* After that is through you can create a new release via `mvn release:perform -Dmaven.java.skip=True`.This will also update the maven repo.
|
||||
* Experimental: mvn deploy -Dregistry=https://maven.pkg.github.com/ZUGFeRD -Dtoken=GH_TOKEN
|
||||
|
||||

|
||||
|
||||
|
||||
@@ -3,13 +3,13 @@
|
||||
<parent>
|
||||
<groupId>org.mustangproject</groupId>
|
||||
<artifactId>core</artifactId>
|
||||
<version>2.0.2-SNAPSHOT</version>
|
||||
<version>2.0.3-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>org.mustangproject</groupId>
|
||||
<artifactId>library</artifactId>
|
||||
<version>2.0.2-SNAPSHOT</version>
|
||||
<version>2.0.3-SNAPSHOT</version>
|
||||
<packaging>jar</packaging>
|
||||
<name>Library to write and read FacturX and ZUGFeRD e-invoices. </name>
|
||||
<description>The Mustang project is a java library to read and write ZUGFeRD meta data inside your invoice PDFs. To write files, a provided PDF/A will be combined with generated or provided XML.
|
||||
|
||||
@@ -40,6 +40,7 @@ public class Invoice implements IExportableTransaction {
|
||||
protected ArrayList<IZUGFeRDExportableItem> ZFItems = null;
|
||||
protected ArrayList<String> notes = null;
|
||||
protected String contractReferencedDocument = null;
|
||||
protected ArrayList<FileAttachment> xmlEmbeddedFiles=null;
|
||||
|
||||
protected Date detailedDeliveryDateStart = null;
|
||||
protected Date detailedDeliveryPeriodEnd = null;
|
||||
@@ -78,6 +79,22 @@ public class Invoice implements IExportableTransaction {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Invoice embedFileInXML(FileAttachment fa) {
|
||||
if (xmlEmbeddedFiles == null) {
|
||||
xmlEmbeddedFiles=new ArrayList<FileAttachment>();
|
||||
}
|
||||
xmlEmbeddedFiles.add(fa);
|
||||
return this;
|
||||
}
|
||||
|
||||
public FileAttachment[] getAdditionalReferencedDocuments() {
|
||||
if (xmlEmbeddedFiles == null) {
|
||||
return null;
|
||||
}
|
||||
return xmlEmbeddedFiles.toArray(new FileAttachment[0]);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getNumber() {
|
||||
|
||||
@@ -10,6 +10,8 @@ import java.math.BigDecimal;
|
||||
public class Product implements IZUGFeRDExportableProduct {
|
||||
protected String unit, name, description, sellerAssignedID, buyerAssignedID;
|
||||
protected BigDecimal VATPercent;
|
||||
protected boolean isReverseCharge=false;
|
||||
protected boolean isIntraCommunitySupply=false;
|
||||
|
||||
/***
|
||||
* default constructor
|
||||
@@ -54,6 +56,37 @@ public class Product implements IZUGFeRDExportableProduct {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isReverseCharge() {
|
||||
return isReverseCharge;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isIntraCommunitySupply() {
|
||||
return isIntraCommunitySupply;
|
||||
}
|
||||
|
||||
/***
|
||||
* sets reverse charge(=delivery to outside EU)
|
||||
* @return fluent setter
|
||||
*/
|
||||
public Product setReverseCharge() {
|
||||
isReverseCharge=true;
|
||||
setVATPercent(BigDecimal.ZERO);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/***
|
||||
* sets intra community supply(=delivery outside the country inside the EU)
|
||||
* @return fluent setter
|
||||
*/
|
||||
public Product setIntraCommunitySupply() {
|
||||
isIntraCommunitySupply=true;
|
||||
setVATPercent(BigDecimal.ZERO);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUnit() {
|
||||
return unit;
|
||||
|
||||
@@ -108,10 +108,16 @@ public interface IZUGFeRDExportableProduct {
|
||||
return false;
|
||||
}
|
||||
|
||||
default boolean isReverseCharge() {
|
||||
return false;
|
||||
}
|
||||
|
||||
default String getTaxCategoryCode() {
|
||||
if (isIntraCommunitySupply()) {
|
||||
return "K";
|
||||
} else if (getVATPercent().equals(new BigDecimal(0))) {
|
||||
return "K"; // within europe
|
||||
} else if (isReverseCharge()) {
|
||||
return "AE"; // to out of europe...
|
||||
} else if (getVATPercent().equals(BigDecimal.ZERO)) {
|
||||
return "Z"; // zero rated goods
|
||||
} else {
|
||||
return "S"; // one of the "standard" rates (not neccessarily a default rate, even a deducted VAT is standard calculation)
|
||||
@@ -119,8 +125,11 @@ public interface IZUGFeRDExportableProduct {
|
||||
}
|
||||
|
||||
default String getTaxExemptionReason() {
|
||||
if (isIntraCommunitySupply())
|
||||
if (isIntraCommunitySupply()) {
|
||||
return "Intra-community supply";
|
||||
} else if (isReverseCharge()) {
|
||||
return "Reverse Charge";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -442,7 +442,7 @@ public class ZUGFeRD2PullProvider implements IXMLProvider {
|
||||
+ " <ram:TypeCode>916</ram:TypeCode>\n"
|
||||
+ " <ram:Name>" + f.getDescription() + "</ram:Name>\n"
|
||||
+ " <ram:AttachmentBinaryObject mimeCode=\"" + f.getMimetype() + "\"\n"
|
||||
+ " filename=\"" + f.getFilename() + "\">" + documentContent + "\n"
|
||||
+ " filename=\"" + f.getFilename() + "\">" + documentContent + "</ram:AttachmentBinaryObject>\n"
|
||||
+ " </ram:AdditionalReferencedDocument>\n";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@ import static org.xmlunit.assertj.XmlAssert.assertThat;
|
||||
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
|
||||
public class XRTest extends TestCase {
|
||||
final String TARGET_XML = "./target/testout-XR.xml";
|
||||
final String TARGET_EDGE_XML = "./target/testout-XR-Edge.xml";
|
||||
public void testXRExport() {
|
||||
|
||||
// the writing part
|
||||
@@ -76,5 +77,48 @@ public class XRTest extends TestCase {
|
||||
}
|
||||
|
||||
}
|
||||
public void testXREdgeExport() {
|
||||
|
||||
// the writing part
|
||||
|
||||
String orgname = "Test company";
|
||||
String number = "123";
|
||||
String amountStr = "1.00";
|
||||
BigDecimal amount = new BigDecimal(amountStr);
|
||||
byte[] b = {12, 13};
|
||||
FileAttachment fe1=new FileAttachment("one.pdf", "application/pdf", "Alternative", b);
|
||||
FileAttachment fe2=new FileAttachment("two.pdf", "application/pdf", "Alternative", b);
|
||||
Invoice i = new Invoice().setDueDate(new Date()).setIssueDate(new Date()).setDeliveryDate(new Date())
|
||||
.setSender(new TradeParty(orgname,"teststr","55232","teststadt","DE").addTaxID("DE4711").addVATID("DE0815").setContact(new Contact("Hans Test","+49123456789","test@example.org")).addBankDetails(new BankDetails("DE12500105170648489890","COBADEFXXX")))
|
||||
.setRecipient(new TradeParty("Franz Müller", "teststr.12", "55232", "Entenhausen", "DE"))
|
||||
.setReferenceNumber("991-01484-64")//leitweg-id
|
||||
// not using any VAT, this is also a test of zero-rated goods:
|
||||
.setNumber(number).addItem(new Item(new Product("Testprodukt", "", "C62", BigDecimal.ZERO), amount, new BigDecimal(1.0)))
|
||||
.embedFileInXML(fe1).embedFileInXML(fe2);
|
||||
|
||||
|
||||
ZUGFeRD2PullProvider zf2p = new ZUGFeRD2PullProvider();
|
||||
|
||||
zf2p.setProfile(Profiles.getByName("XRechnung"));
|
||||
zf2p.generateXML(i);
|
||||
String theXML = new String(zf2p.getXML());
|
||||
assertTrue(theXML.contains("<rsm:CrossIndustryInvoice"));
|
||||
assertThat(theXML).valueByXPath("count(//*[local-name()='IncludedSupplyChainTradeLineItem'])")
|
||||
.asInt()
|
||||
.isEqualTo(1); //2 errors are OK because there is a known bug
|
||||
|
||||
|
||||
assertThat(theXML).valueByXPath("//*[local-name()='DuePayableAmount']")
|
||||
.asDouble()
|
||||
.isEqualTo(1);
|
||||
try {
|
||||
BufferedWriter writer = new BufferedWriter(new FileWriter(TARGET_EDGE_XML));
|
||||
writer.write(theXML);
|
||||
writer.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -45,6 +45,8 @@ public class ZF2PushTest extends TestCase {
|
||||
final String TARGET_RELATIVECHARGESALLOWANCESPDF = "./target/testout-ZF2PushRelativeChargesAllowances.pdf";
|
||||
final String TARGET_ATTACHMENTSPDF = "./target/testout-ZF2PushAttachments.pdf";
|
||||
final String TARGET_PUSHEDGE = "./target/testout-ZF2PushEdge.pdf";
|
||||
final String TARGET_INTRACOMMUNITYSUPPLYPDF = "./target/testout-ZF2PushIntraCommunitySupply.pdf";
|
||||
final String TARGET_REVERSECHARGEPDF = "./target/testout-ZF2PushReverseCharge.pdf";
|
||||
|
||||
public void testPushExport() {
|
||||
|
||||
@@ -117,8 +119,8 @@ public class ZF2PushTest extends TestCase {
|
||||
.load(SOURCE_PDF)) {
|
||||
|
||||
byte[] b = {12, 13};
|
||||
ze.attachFile("one.pdf", b, "Application/PDF", "Alternative");
|
||||
ze.attachFile("two.pdf", b, "Application/PDF", "Alternative");
|
||||
ze.attachFile("one.pdf", b, "application/pdf", "Alternative");
|
||||
ze.attachFile("two.pdf", b, "application/pdf", "Alternative");
|
||||
ze.setTransaction(new Invoice().setDueDate(new Date()).setIssueDate(new Date()).setDeliveryDate(new Date()).setSender(new TradeParty(orgname, "teststr", "55232", "teststadt", "DE").addTaxID(taxID)).setOwnVATID("DE0815").setRecipient(new TradeParty("Franz Müller", "teststr.12", "55232", "Entenhausen", "DE").addVATID("DE4711").setContact(new Contact("Franz Müller", "01779999999", "franz@mueller.de", "teststr. 12", "55232", "Entenhausen", "DE"))).setNumber(number).addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), price, new BigDecimal(1.0)))
|
||||
|
||||
);
|
||||
@@ -198,6 +200,108 @@ public class ZF2PushTest extends TestCase {
|
||||
|
||||
}
|
||||
|
||||
|
||||
public void testIntraCommunitySupplyExport() {
|
||||
|
||||
String orgname = "Test company";
|
||||
String number = "123";
|
||||
String amountStr = "3.00";
|
||||
BigDecimal amount = new BigDecimal(amountStr);
|
||||
try (InputStream SOURCE_PDF = this.getClass()
|
||||
.getResourceAsStream("/MustangGnuaccountingBeispielRE-20170509_505blanko.pdf");
|
||||
|
||||
ZUGFeRDExporterFromA1 ze = new ZUGFeRDExporterFromA1().setProducer("My Application")
|
||||
.setCreator(System.getProperty("user.name")).setZUGFeRDVersion(2).setProfile("extended").ignorePDFAErrors()
|
||||
.load(SOURCE_PDF)) {
|
||||
// ze.setTransaction(new Invoice().setDueDate(new Date()).setIssueDate(new Date()).setDeliveryDate(new Date()).setSender(new TradeParty(orgname,"teststr", "55232","teststadt","DE")).setOwnTaxID("4711").setOwnVATID("DE0815").setRecipient(new TradeParty("Franz Müller", "teststr.12", "55232", "Entenhausen", "DE")).setNumber(number)
|
||||
// .addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), amount, new BigDecimal(1.0)).addAllowance(new Allowance().setPercent(new BigDecimal(50)))));
|
||||
|
||||
|
||||
ze.setTransaction(new Invoice().setDueDate(new Date()).setIssueDate(new Date()).setDeliveryDate(new Date())
|
||||
.setSender(new TradeParty(orgname, "teststr", "55232", "teststadt", "DE").addVATID("DE0815")).setOwnTaxID("4711")
|
||||
.setRecipient(new TradeParty("Franz Müller", "teststr.12", "55232", "Entenhausen", "DE").addVATID("DE0816").setContact(new Contact("contact testname", "123456", "contact.testemail@example.org").setFax("0911623562")))
|
||||
.setDeliveryAddress(new TradeParty("Franz Müller", "teststr.12", "55232", "Entenhausen", "DE").addVATID("DE0816"))
|
||||
.setNumber(number)
|
||||
.addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)).setIntraCommunitySupply(), amount, new BigDecimal(1.0)))
|
||||
.addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)).setIntraCommunitySupply(), amount, new BigDecimal(1.0)))
|
||||
.addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)).setIntraCommunitySupply(), amount, new BigDecimal(2.0)))
|
||||
.addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)).setIntraCommunitySupply(), amount, new BigDecimal(1.0)))
|
||||
);
|
||||
|
||||
String theXML = new String(ze.getProvider().getXML());
|
||||
assertTrue(theXML.contains("<rsm:CrossIndustryInvoice"));
|
||||
ze.export(TARGET_INTRACOMMUNITYSUPPLYPDF);
|
||||
} catch (IOException e) {
|
||||
fail("IOException should not be raised in testEdgeExport");
|
||||
}
|
||||
|
||||
// now check the contents (like MustangReaderTest)
|
||||
ZUGFeRDImporter zi = new ZUGFeRDImporter(TARGET_INTRACOMMUNITYSUPPLYPDF);
|
||||
|
||||
assertTrue(zi.getUTF8().contains("EUR"));
|
||||
assertTrue(zi.getUTF8().contains("0911623562")); // fax number
|
||||
|
||||
// Reading ZUGFeRD
|
||||
assertEquals("15.00", zi.getAmount());
|
||||
assertEquals(zi.getHolder(), orgname);
|
||||
assertEquals(zi.getForeignReference(), number);
|
||||
try {
|
||||
assertEquals(zi.getVersion(), 2);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
public void testReverseChargeExport() {
|
||||
|
||||
String orgname = "Test company";
|
||||
String number = "123";
|
||||
String amountStr = "3.00";
|
||||
BigDecimal amount = new BigDecimal(amountStr);
|
||||
try (InputStream SOURCE_PDF = this.getClass()
|
||||
.getResourceAsStream("/MustangGnuaccountingBeispielRE-20170509_505blanko.pdf");
|
||||
|
||||
ZUGFeRDExporterFromA1 ze = new ZUGFeRDExporterFromA1().setProducer("My Application")
|
||||
.setCreator(System.getProperty("user.name")).setZUGFeRDVersion(2).setProfile("extended").ignorePDFAErrors()
|
||||
.load(SOURCE_PDF)) {
|
||||
// ze.setTransaction(new Invoice().setDueDate(new Date()).setIssueDate(new Date()).setDeliveryDate(new Date()).setSender(new TradeParty(orgname,"teststr", "55232","teststadt","DE")).setOwnTaxID("4711").setOwnVATID("DE0815").setRecipient(new TradeParty("Franz Müller", "teststr.12", "55232", "Entenhausen", "DE")).setNumber(number)
|
||||
// .addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), amount, new BigDecimal(1.0)).addAllowance(new Allowance().setPercent(new BigDecimal(50)))));
|
||||
|
||||
|
||||
ze.setTransaction(new Invoice().setDueDate(new Date()).setIssueDate(new Date()).setDeliveryDate(new Date()).setSender(new TradeParty(orgname, "teststr", "55232", "teststadt", "DE").addVATID("DE0815")).setOwnTaxID("4711").setRecipient(new TradeParty("Franz Müller", "teststr.12", "55232", "Entenhausen", "DE").addVATID("DE0816").setContact(new Contact("contact testname", "123456", "contact.testemail@example.org").setFax("0911623562"))).setNumber(number)
|
||||
.addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)).setReverseCharge(), amount, new BigDecimal(1.0)))
|
||||
.addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)).setReverseCharge(), amount, new BigDecimal(1.0)))
|
||||
.addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)).setReverseCharge(), amount, new BigDecimal(2.0)))
|
||||
.addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)).setReverseCharge(), amount, new BigDecimal(1.0)))
|
||||
);
|
||||
|
||||
String theXML = new String(ze.getProvider().getXML());
|
||||
assertTrue(theXML.contains("<rsm:CrossIndustryInvoice"));
|
||||
ze.export(TARGET_REVERSECHARGEPDF);
|
||||
} catch (IOException e) {
|
||||
fail("IOException should not be raised in testEdgeExport");
|
||||
}
|
||||
|
||||
// now check the contents (like MustangReaderTest)
|
||||
ZUGFeRDImporter zi = new ZUGFeRDImporter(TARGET_REVERSECHARGEPDF);
|
||||
|
||||
assertTrue(zi.getUTF8().contains("EUR"));
|
||||
assertTrue(zi.getUTF8().contains("0911623562")); // fax number
|
||||
|
||||
// Reading ZUGFeRD
|
||||
assertEquals("15.00", zi.getAmount());
|
||||
assertEquals(zi.getHolder(), orgname);
|
||||
assertEquals(zi.getForeignReference(), number);
|
||||
try {
|
||||
assertEquals(zi.getVersion(), 2);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
public void testChargesAllowancesExport() {
|
||||
|
||||
String orgname = "Test company";
|
||||
|
||||
2
pom.xml
2
pom.xml
@@ -3,7 +3,7 @@
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>org.mustangproject</groupId>
|
||||
<artifactId>core</artifactId>
|
||||
<version>2.0.2-SNAPSHOT</version>
|
||||
<version>2.0.3-SNAPSHOT</version>
|
||||
<packaging>pom</packaging>
|
||||
<name>Mustang</name>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>org.mustangproject</groupId>
|
||||
<artifactId>core</artifactId>
|
||||
<version>2.0.2-SNAPSHOT</version>
|
||||
<version>2.0.3-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>org.mustangproject</groupId>
|
||||
@@ -11,7 +11,7 @@
|
||||
<name>Library to validate e-invoices (ZUGFeRD, Factur-X and Xrechnung)</name>
|
||||
|
||||
<packaging>jar</packaging>
|
||||
<version>2.0.2-SNAPSHOT</version>
|
||||
<version>2.0.3-SNAPSHOT</version>
|
||||
<repositories>
|
||||
<repository>
|
||||
<!-- for jargs -->
|
||||
@@ -75,7 +75,7 @@
|
||||
<dependency>
|
||||
<groupId>org.mustangproject</groupId>
|
||||
<artifactId>library</artifactId>
|
||||
<version>2.0.2-SNAPSHOT</version>
|
||||
<version>2.0.3-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.xmlunit</groupId>
|
||||
|
||||
@@ -157,7 +157,7 @@ public class LibraryTest extends ResourceCase {
|
||||
* automatically test the xrechnung
|
||||
*/
|
||||
public void testXRValidation() {
|
||||
File tempFile = new File("../library/target/testout-XR.xml");
|
||||
File tempFile = new File("../library/target/testout-XR-Edge.xml");
|
||||
ZUGFeRDValidator zfv = new ZUGFeRDValidator();
|
||||
|
||||
String res = zfv.validate(tempFile.getAbsolutePath());
|
||||
|
||||
Reference in New Issue
Block a user