use of org.apache.ofbiz.entity.condition.EntityCondition in project ofbiz-framework by apache.
the class EntitySyncContext method assembleValuesToCreate.
public ArrayList<GenericValue> assembleValuesToCreate() throws SyncDataErrorException {
// first grab all values inserted in the date range, then get the updates (leaving out all values inserted in the data range)
// make it an ArrayList to easily merge in sorted lists
ArrayList<GenericValue> valuesToCreate = new ArrayList<GenericValue>();
if (this.nextCreateTxTime != null && (this.nextCreateTxTime.equals(currentRunEndTime) || this.nextCreateTxTime.after(currentRunEndTime))) {
// this means that for all entities in this pack we found on the last pass that there would be nothing for this one, so just return nothing...
return valuesToCreate;
}
// Debug.logInfo("Getting values to create; currentRunStartTime=" + currentRunStartTime + ", currentRunEndTime=" + currentRunEndTime, module);
int entitiesSkippedForKnownNext = 0;
// iterate through entities, get all records with tx stamp in the current time range, put all in a single list
for (ModelEntity modelEntity : entityModelToUseList) {
int insertBefore = 0;
// first test to see if we know that there are no records for this entity in this time period...
Timestamp knownNextCreateTime = this.nextEntityCreateTxTime.get(modelEntity.getEntityName());
if (knownNextCreateTime != null && (knownNextCreateTime.equals(currentRunEndTime) || knownNextCreateTime.after(currentRunEndTime))) {
// Debug.logInfo("In assembleValuesToCreate found knownNextCreateTime [" + knownNextCreateTime + "] after currentRunEndTime [" + currentRunEndTime + "], so skipping time per period for entity [" + modelEntity.getEntityName() + "]", module);
entitiesSkippedForKnownNext++;
continue;
}
boolean beganTransaction = false;
try {
beganTransaction = TransactionUtil.begin(7200);
} catch (GenericTransactionException e) {
throw new SyncDataErrorException("Unable to begin JTA transaction", e);
}
try {
EntityCondition findValCondition = EntityCondition.makeCondition(EntityCondition.makeCondition(ModelEntity.CREATE_STAMP_TX_FIELD, EntityOperator.GREATER_THAN_EQUAL_TO, currentRunStartTime), EntityCondition.makeCondition(ModelEntity.CREATE_STAMP_TX_FIELD, EntityOperator.LESS_THAN, currentRunEndTime));
EntityQuery eq = EntityQuery.use(delegator).from(modelEntity.getEntityName()).where(findValCondition).orderBy(ModelEntity.CREATE_STAMP_TX_FIELD, ModelEntity.CREATE_STAMP_FIELD);
long valuesPerEntity = 0;
try (EntityListIterator eli = eq.queryIterator()) {
// get the values created within the current time range
GenericValue nextValue = null;
while ((nextValue = eli.next()) != null) {
// find first value in valuesToCreate list, starting with the current insertBefore value, that has a CREATE_STAMP_TX_FIELD after the nextValue.CREATE_STAMP_TX_FIELD, then do the same with CREATE_STAMP_FIELD
while (insertBefore < valuesToCreate.size() && valuesToCreate.get(insertBefore).getTimestamp(ModelEntity.CREATE_STAMP_TX_FIELD).before(nextValue.getTimestamp(ModelEntity.CREATE_STAMP_TX_FIELD))) {
insertBefore++;
}
while (insertBefore < valuesToCreate.size() && valuesToCreate.get(insertBefore).getTimestamp(ModelEntity.CREATE_STAMP_FIELD).before(nextValue.getTimestamp(ModelEntity.CREATE_STAMP_FIELD))) {
insertBefore++;
}
valuesToCreate.add(insertBefore, nextValue);
valuesPerEntity++;
}
} catch (GenericEntityException e) {
try {
TransactionUtil.rollback(beganTransaction, "Entity Engine error in assembleValuesToCreate", e);
} catch (GenericTransactionException e2) {
Debug.logWarning(e2, "Unable to call rollback()", module);
}
throw new SyncDataErrorException("Error getting values to create from the datasource", e);
}
// if we didn't find anything for this entity, find the next value's Timestamp and keep track of it
if (valuesPerEntity == 0) {
Timestamp startCheckStamp = new Timestamp(System.currentTimeMillis() - syncEndBufferMillis);
EntityCondition findNextCondition = EntityCondition.makeCondition(EntityCondition.makeCondition(ModelEntity.CREATE_STAMP_TX_FIELD, EntityOperator.NOT_EQUAL, null), EntityCondition.makeCondition(ModelEntity.CREATE_STAMP_TX_FIELD, EntityOperator.GREATER_THAN_EQUAL_TO, currentRunEndTime));
eq = EntityQuery.use(delegator).from(modelEntity.getEntityName()).where(findNextCondition).orderBy(ModelEntity.CREATE_STAMP_TX_FIELD);
GenericValue firstVal = null;
try (EntityListIterator eliNext = eq.queryIterator()) {
// get the first element and it's tx time value...
firstVal = eliNext.next();
} catch (GenericEntityException e) {
try {
TransactionUtil.rollback(beganTransaction, "Entity Engine error in assembleValuesToCreate", e);
} catch (GenericTransactionException e2) {
Debug.logWarning(e2, "Unable to call rollback()", module);
}
throw new SyncDataErrorException("Error getting values to create from the datasource", e);
}
Timestamp nextTxTime;
if (firstVal != null) {
nextTxTime = firstVal.getTimestamp(ModelEntity.CREATE_STAMP_TX_FIELD);
} else {
// no results? well, then it's safe to say that up to the pre-querytime (minus the buffer, as usual) we are okay
nextTxTime = startCheckStamp;
}
if (this.nextCreateTxTime == null || nextTxTime.before(this.nextCreateTxTime)) {
this.nextCreateTxTime = nextTxTime;
Debug.logInfo("EntitySync: Set nextCreateTxTime to [" + nextTxTime + "]", module);
}
Timestamp curEntityNextTxTime = this.nextEntityCreateTxTime.get(modelEntity.getEntityName());
if (curEntityNextTxTime == null || nextTxTime.before(curEntityNextTxTime)) {
this.nextEntityCreateTxTime.put(modelEntity.getEntityName(), nextTxTime);
Debug.logInfo("EntitySync: Set nextEntityCreateTxTime to [" + nextTxTime + "] for the entity [" + modelEntity.getEntityName() + "]", module);
}
}
} catch (Throwable t) {
try {
TransactionUtil.rollback(beganTransaction, "Throwable error in assembleValuesToCreate", t);
} catch (GenericTransactionException e2) {
Debug.logWarning(e2, "Unable to call rollback()", module);
}
throw new SyncDataErrorException("Caught runtime error while getting values to create", t);
}
try {
TransactionUtil.commit(beganTransaction);
} catch (GenericTransactionException e) {
throw new SyncDataErrorException("Commit transaction failed", e);
}
}
if (entitiesSkippedForKnownNext > 0) {
if (Debug.infoOn())
Debug.logInfo("In assembleValuesToCreate skipped [" + entitiesSkippedForKnownNext + "/" + entityModelToUseList + "] entities for the time period ending at [" + currentRunEndTime + "] because of next known create times", module);
}
// TEST SECTION: leave false for normal use
boolean logValues = false;
if (logValues && valuesToCreate.size() > 0) {
StringBuilder toCreateInfo = new StringBuilder();
for (GenericValue valueToCreate : valuesToCreate) {
toCreateInfo.append("\n-->[");
toCreateInfo.append(valueToCreate.get(ModelEntity.CREATE_STAMP_TX_FIELD));
toCreateInfo.append(":");
toCreateInfo.append(valueToCreate.get(ModelEntity.CREATE_STAMP_FIELD));
toCreateInfo.append("] ");
toCreateInfo.append(valueToCreate.getPrimaryKey());
}
Debug.logInfo(toCreateInfo.toString(), module);
}
// this calculation is false, so it needs to be nullified
if (valuesToCreate.size() > 0) {
this.nextCreateTxTime = null;
}
return valuesToCreate;
}
use of org.apache.ofbiz.entity.condition.EntityCondition in project ofbiz-framework by apache.
the class ServiceUtil method purgeOldJobs.
public static Map<String, Object> purgeOldJobs(DispatchContext dctx, Map<String, ? extends Object> context) {
Locale locale = (Locale) context.get("locale");
Debug.logWarning("purgeOldJobs service invoked. This service is obsolete - the Job Scheduler will purge old jobs automatically.", module);
String sendPool = null;
Calendar cal = Calendar.getInstance();
try {
sendPool = ServiceConfigUtil.getServiceEngine().getThreadPool().getSendToPool();
int daysToKeep = ServiceConfigUtil.getServiceEngine().getThreadPool().getPurgeJobDays();
cal.add(Calendar.DAY_OF_YEAR, -daysToKeep);
} catch (GenericConfigException e) {
Debug.logWarning(e, "Exception thrown while getting service configuration: ", module);
return returnError(UtilProperties.getMessage(ServiceUtil.resource, "ServiceExceptionThrownWhileGettingServiceConfiguration", UtilMisc.toMap("errorString", e), locale));
}
Delegator delegator = dctx.getDelegator();
Timestamp purgeTime = new Timestamp(cal.getTimeInMillis());
// create the conditions to query
EntityCondition pool = EntityCondition.makeCondition("poolId", sendPool);
List<EntityExpr> finExp = UtilMisc.toList(EntityCondition.makeCondition("finishDateTime", EntityOperator.NOT_EQUAL, null));
finExp.add(EntityCondition.makeCondition("finishDateTime", EntityOperator.LESS_THAN, purgeTime));
List<EntityExpr> canExp = UtilMisc.toList(EntityCondition.makeCondition("cancelDateTime", EntityOperator.NOT_EQUAL, null));
canExp.add(EntityCondition.makeCondition("cancelDateTime", EntityOperator.LESS_THAN, purgeTime));
EntityCondition cancelled = EntityCondition.makeCondition(canExp);
EntityCondition finished = EntityCondition.makeCondition(finExp);
EntityCondition doneCond = EntityCondition.makeCondition(UtilMisc.toList(cancelled, finished), EntityOperator.OR);
// always suspend the current transaction; use the one internally
Transaction parent = null;
try {
if (TransactionUtil.getStatus() != TransactionUtil.STATUS_NO_TRANSACTION) {
parent = TransactionUtil.suspend();
}
// lookup the jobs - looping 1000 at a time to avoid problems with cursors
// also, using unique transaction to delete as many as possible even with errors
boolean noMoreResults = false;
boolean beganTx1 = false;
while (!noMoreResults) {
// current list of records
List<GenericValue> curList = null;
try {
// begin this transaction
beganTx1 = TransactionUtil.begin();
EntityQuery eq = EntityQuery.use(delegator).select("jobId").from("JobSandbox").where(EntityCondition.makeCondition(UtilMisc.toList(doneCond, pool))).cursorScrollInsensitive().maxRows(1000);
try (EntityListIterator foundJobs = eq.queryIterator()) {
curList = foundJobs.getPartialList(1, 1000);
}
} catch (GenericEntityException e) {
Debug.logError(e, "Cannot obtain job data from datasource", module);
try {
TransactionUtil.rollback(beganTx1, e.getMessage(), e);
} catch (GenericTransactionException e1) {
Debug.logWarning(e1, module);
}
return ServiceUtil.returnError(e.getMessage());
} finally {
try {
TransactionUtil.commit(beganTx1);
} catch (GenericTransactionException e) {
Debug.logWarning(e, module);
}
}
// remove each from the list in its own transaction
if (UtilValidate.isNotEmpty(curList)) {
for (GenericValue job : curList) {
String jobId = job.getString("jobId");
boolean beganTx2 = false;
try {
beganTx2 = TransactionUtil.begin();
job.remove();
} catch (GenericEntityException e) {
Debug.logInfo("Cannot remove job data for ID: " + jobId, module);
try {
TransactionUtil.rollback(beganTx2, e.getMessage(), e);
} catch (GenericTransactionException e1) {
Debug.logWarning(e1, module);
}
} finally {
try {
TransactionUtil.commit(beganTx2);
} catch (GenericTransactionException e) {
Debug.logWarning(e, module);
}
}
}
} else {
noMoreResults = true;
}
}
// Now JobSandbox data is cleaned up. Now process Runtime data and remove the whole data in single shot that is of no need.
boolean beganTx3 = false;
GenericValue runtimeData = null;
List<GenericValue> runtimeDataToDelete = new LinkedList<>();
long jobsandBoxCount = 0;
try {
// begin this transaction
beganTx3 = TransactionUtil.begin();
EntityQuery eq = EntityQuery.use(delegator).select("runtimeDataId").from("RuntimeData");
try (EntityListIterator runTimeDataIt = eq.queryIterator()) {
while ((runtimeData = runTimeDataIt.next()) != null) {
EntityCondition whereCondition = EntityCondition.makeCondition(UtilMisc.toList(EntityCondition.makeCondition("runtimeDataId", EntityOperator.NOT_EQUAL, null), EntityCondition.makeCondition("runtimeDataId", EntityOperator.EQUALS, runtimeData.getString("runtimeDataId"))), EntityOperator.AND);
jobsandBoxCount = EntityQuery.use(delegator).from("JobSandbox").where(whereCondition).queryCount();
if (BigDecimal.ZERO.compareTo(BigDecimal.valueOf(jobsandBoxCount)) == 0) {
runtimeDataToDelete.add(runtimeData);
}
}
}
// Now we are ready to delete runtimeData, we can safely delete complete list that we have recently fetched i.e runtimeDataToDelete.
delegator.removeAll(runtimeDataToDelete);
} catch (GenericEntityException e) {
Debug.logError(e, "Cannot obtain runtime data from datasource", module);
try {
TransactionUtil.rollback(beganTx3, e.getMessage(), e);
} catch (GenericTransactionException e1) {
Debug.logWarning(e1, module);
}
return ServiceUtil.returnError(e.getMessage());
} finally {
try {
TransactionUtil.commit(beganTx3);
} catch (GenericTransactionException e) {
Debug.logWarning(e, module);
}
}
} catch (GenericTransactionException e) {
Debug.logError(e, "Unable to suspend transaction; cannot purge jobs!", module);
return ServiceUtil.returnError(e.getMessage());
} finally {
if (parent != null) {
try {
TransactionUtil.resume(parent);
} catch (GenericTransactionException e) {
Debug.logWarning(e, module);
}
}
}
return ServiceUtil.returnSuccess();
}
use of org.apache.ofbiz.entity.condition.EntityCondition in project ofbiz-framework by apache.
the class WebToolsServices method entityExportAll.
public static Map<String, Object> entityExportAll(DispatchContext dctx, Map<String, ? extends Object> context) {
Delegator delegator = dctx.getDelegator();
Locale locale = (Locale) context.get("locale");
// mandatory
String outpath = (String) context.get("outpath");
Timestamp fromDate = (Timestamp) context.get("fromDate");
Integer txTimeout = (Integer) context.get("txTimeout");
if (txTimeout == null) {
txTimeout = Integer.valueOf(7200);
}
List<String> results = new LinkedList<String>();
if (UtilValidate.isNotEmpty(outpath)) {
File outdir = new File(outpath);
if (!outdir.exists()) {
outdir.mkdir();
}
if (outdir.isDirectory() && outdir.canWrite()) {
Set<String> passedEntityNames;
try {
ModelReader reader = delegator.getModelReader();
Collection<String> ec = reader.getEntityNames();
passedEntityNames = new TreeSet<String>(ec);
} catch (Exception exc) {
return ServiceUtil.returnError(UtilProperties.getMessage(resource, "EntityImportErrorRetrievingEntityNames", locale));
}
int fileNumber = 1;
for (String curEntityName : passedEntityNames) {
long numberWritten = 0;
ModelEntity me = delegator.getModelEntity(curEntityName);
if (me instanceof ModelViewEntity) {
results.add("[" + fileNumber + "] [vvv] " + curEntityName + " skipping view entity");
continue;
}
List<EntityCondition> conds = new LinkedList<EntityCondition>();
if (UtilValidate.isNotEmpty(fromDate)) {
conds.add(EntityCondition.makeCondition("createdStamp", EntityOperator.GREATER_THAN_EQUAL_TO, fromDate));
}
EntityQuery eq = EntityQuery.use(delegator).from(curEntityName).where(conds).orderBy(me.getPkFieldNames());
try {
boolean beganTx = TransactionUtil.begin();
// Don't bother writing the file if there's nothing to put into it
try (EntityListIterator values = eq.queryIterator()) {
GenericValue value = values.next();
if (value != null) {
try (PrintWriter writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(new FileOutputStream(new File(outdir, curEntityName + ".xml")), "UTF-8")))) {
writer.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
writer.println("<entity-engine-xml>");
do {
value.writeXmlText(writer, "");
numberWritten++;
if (numberWritten % 500 == 0) {
TransactionUtil.commit(beganTx);
beganTx = TransactionUtil.begin();
}
} while ((value = values.next()) != null);
writer.println("</entity-engine-xml>");
} catch (UnsupportedEncodingException | FileNotFoundException e) {
results.add("[" + fileNumber + "] [xxx] Error when writing " + curEntityName + ": " + e);
}
results.add("[" + fileNumber + "] [" + numberWritten + "] " + curEntityName + " wrote " + numberWritten + " records");
} else {
results.add("[" + fileNumber + "] [---] " + curEntityName + " has no records, not writing file");
}
TransactionUtil.commit(beganTx);
} catch (GenericEntityException entityEx) {
results.add("[" + fileNumber + "] [xxx] Error when writing " + curEntityName + ": " + entityEx);
continue;
}
fileNumber++;
} catch (GenericTransactionException e) {
Debug.logError(e, module);
results.add(e.getLocalizedMessage());
}
}
} else {
results.add("Path not found or no write access.");
}
} else {
results.add("No path specified, doing nothing.");
}
// send the notification
Map<String, Object> resp = UtilMisc.<String, Object>toMap("results", results);
return resp;
}
use of org.apache.ofbiz.entity.condition.EntityCondition in project ofbiz-framework by apache.
the class InvoiceServices method createInvoiceForOrder.
/* Service to create an invoice for an order */
public static Map<String, Object> createInvoiceForOrder(DispatchContext dctx, Map<String, Object> context) {
Delegator delegator = dctx.getDelegator();
LocalDispatcher dispatcher = dctx.getDispatcher();
GenericValue userLogin = (GenericValue) context.get("userLogin");
Locale locale = (Locale) context.get("locale");
if (DECIMALS == -1 || ROUNDING == null) {
return ServiceUtil.returnError(UtilProperties.getMessage(resource, "AccountingAritmeticPropertiesNotConfigured", locale));
}
String orderId = (String) context.get("orderId");
List<GenericValue> billItems = UtilGenerics.checkList(context.get("billItems"));
String invoiceId = (String) context.get("invoiceId");
if (UtilValidate.isEmpty(billItems)) {
if (Debug.verboseOn())
Debug.logVerbose("No order items to invoice; not creating invoice; returning success", module);
return ServiceUtil.returnSuccess(UtilProperties.getMessage(resource, "AccountingNoOrderItemsToInvoice", locale));
}
try {
GenericValue orderHeader = EntityQuery.use(delegator).from("OrderHeader").where("orderId", orderId).queryOne();
if (orderHeader == null) {
return ServiceUtil.returnError(UtilProperties.getMessage(resource, "AccountingNoOrderHeader", locale));
}
// figure out the invoice type
String invoiceType = null;
String orderType = orderHeader.getString("orderTypeId");
if ("SALES_ORDER".equals(orderType)) {
invoiceType = "SALES_INVOICE";
} else if ("PURCHASE_ORDER".equals(orderType)) {
invoiceType = "PURCHASE_INVOICE";
}
// Set the precision depending on the type of invoice
int invoiceTypeDecimals = UtilNumber.getBigDecimalScale("invoice." + invoiceType + ".decimals");
if (invoiceTypeDecimals == -1) {
invoiceTypeDecimals = DECIMALS;
}
// Make an order read helper from the order
OrderReadHelper orh = new OrderReadHelper(orderHeader);
// get the product store
GenericValue productStore = orh.getProductStore();
// get the shipping adjustment mode (Y = Pro-Rate; N = First-Invoice)
String prorateShipping = productStore != null ? productStore.getString("prorateShipping") : "Y";
if (prorateShipping == null) {
prorateShipping = "Y";
}
// get the billing parties
String billToCustomerPartyId = orh.getBillToParty().getString("partyId");
String billFromVendorPartyId = orh.getBillFromParty().getString("partyId");
// get some price totals
BigDecimal shippableAmount = orh.getShippableTotal(null);
BigDecimal shippableQuantity = orh.getShippableQuantity(null);
BigDecimal orderSubTotal = orh.getOrderItemsSubTotal();
BigDecimal orderQuantity = orh.getTotalOrderItemsQuantity();
// these variables are for pro-rating order amounts across invoices, so they should not be rounded off for maximum accuracy
BigDecimal invoiceShipProRateAmount = BigDecimal.ZERO;
BigDecimal invoiceShippableQuantity = BigDecimal.ZERO;
BigDecimal invoiceSubTotal = BigDecimal.ZERO;
BigDecimal invoiceQuantity = BigDecimal.ZERO;
GenericValue billingAccount = orderHeader.getRelatedOne("BillingAccount", false);
String billingAccountId = billingAccount != null ? billingAccount.getString("billingAccountId") : null;
Timestamp invoiceDate = (Timestamp) context.get("eventDate");
if (UtilValidate.isEmpty(invoiceDate)) {
// TODO: ideally this should be the same time as when a shipment is sent and be passed in as a parameter
invoiceDate = UtilDateTime.nowTimestamp();
}
// TODO: perhaps consider billing account net days term as well?
Long orderTermNetDays = orh.getOrderTermNetDays();
Timestamp dueDate = null;
if (orderTermNetDays != null) {
dueDate = UtilDateTime.getDayEnd(invoiceDate, orderTermNetDays);
}
// create the invoice record
if (UtilValidate.isEmpty(invoiceId)) {
Map<String, Object> createInvoiceContext = new HashMap<>();
createInvoiceContext.put("partyId", billToCustomerPartyId);
createInvoiceContext.put("partyIdFrom", billFromVendorPartyId);
createInvoiceContext.put("billingAccountId", billingAccountId);
createInvoiceContext.put("invoiceDate", invoiceDate);
createInvoiceContext.put("dueDate", dueDate);
createInvoiceContext.put("invoiceTypeId", invoiceType);
// start with INVOICE_IN_PROCESS, in the INVOICE_READY we can't change the invoice (or shouldn't be able to...)
createInvoiceContext.put("statusId", "INVOICE_IN_PROCESS");
createInvoiceContext.put("currencyUomId", orderHeader.getString("currencyUom"));
createInvoiceContext.put("userLogin", userLogin);
// store the invoice first
Map<String, Object> createInvoiceResult = dispatcher.runSync("createInvoice", createInvoiceContext);
if (ServiceUtil.isError(createInvoiceResult)) {
return ServiceUtil.returnError(UtilProperties.getMessage(resource, "AccountingErrorCreatingInvoiceFromOrder", locale), null, null, createInvoiceResult);
}
// call service, not direct entity op: delegator.create(invoice);
invoiceId = (String) createInvoiceResult.get("invoiceId");
}
// order roles to invoice roles
List<GenericValue> orderRoles = orderHeader.getRelated("OrderRole", null, null, false);
Map<String, Object> createInvoiceRoleContext = new HashMap<>();
createInvoiceRoleContext.put("invoiceId", invoiceId);
createInvoiceRoleContext.put("userLogin", userLogin);
for (GenericValue orderRole : orderRoles) {
createInvoiceRoleContext.put("partyId", orderRole.getString("partyId"));
createInvoiceRoleContext.put("roleTypeId", orderRole.getString("roleTypeId"));
Map<String, Object> createInvoiceRoleResult = dispatcher.runSync("createInvoiceRole", createInvoiceRoleContext);
if (ServiceUtil.isError(createInvoiceRoleResult)) {
return ServiceUtil.returnError(UtilProperties.getMessage(resource, "AccountingErrorCreatingInvoiceFromOrder", locale), null, null, createInvoiceRoleResult);
}
}
// order terms to invoice terms.
// TODO: it might be nice to filter OrderTerms to only copy over financial terms.
List<GenericValue> orderTerms = orh.getOrderTerms();
createInvoiceTerms(delegator, dispatcher, invoiceId, orderTerms, userLogin, locale);
// for billing accounts we will use related information
if (billingAccount != null) {
/*
* jacopoc: billing account terms were already copied as order terms
* when the order was created.
// get the billing account terms
billingAccountTerms = billingAccount.getRelated("BillingAccountTerm", null, null, false);
// set the invoice terms as defined for the billing account
createInvoiceTerms(delegator, dispatcher, invoiceId, billingAccountTerms, userLogin, locale);
*/
// set the invoice bill_to_customer from the billing account
List<GenericValue> billToRoles = billingAccount.getRelated("BillingAccountRole", UtilMisc.toMap("roleTypeId", "BILL_TO_CUSTOMER"), null, false);
for (GenericValue billToRole : billToRoles) {
if (!(billToRole.getString("partyId").equals(billToCustomerPartyId))) {
createInvoiceRoleContext = UtilMisc.toMap("invoiceId", invoiceId, "partyId", billToRole.get("partyId"), "roleTypeId", "BILL_TO_CUSTOMER", "userLogin", userLogin);
Map<String, Object> createInvoiceRoleResult = dispatcher.runSync("createInvoiceRole", createInvoiceRoleContext);
if (ServiceUtil.isError(createInvoiceRoleResult)) {
return ServiceUtil.returnError(UtilProperties.getMessage(resource, "AccountingErrorCreatingInvoiceRoleFromOrder", locale), null, null, createInvoiceRoleResult);
}
}
}
// set the bill-to contact mech as the contact mech of the billing account
if (UtilValidate.isNotEmpty(billingAccount.getString("contactMechId"))) {
Map<String, Object> createBillToContactMechContext = UtilMisc.toMap("invoiceId", invoiceId, "contactMechId", billingAccount.getString("contactMechId"), "contactMechPurposeTypeId", "BILLING_LOCATION", "userLogin", userLogin);
Map<String, Object> createBillToContactMechResult = dispatcher.runSync("createInvoiceContactMech", createBillToContactMechContext);
if (ServiceUtil.isError(createBillToContactMechResult)) {
return ServiceUtil.returnError(UtilProperties.getMessage(resource, "AccountingErrorCreatingInvoiceContactMechFromOrder", locale), null, null, createBillToContactMechResult);
}
}
} else {
List<GenericValue> billingLocations = orh.getBillingLocations();
if (UtilValidate.isNotEmpty(billingLocations)) {
for (GenericValue ocm : billingLocations) {
Map<String, Object> createBillToContactMechContext = UtilMisc.toMap("invoiceId", invoiceId, "contactMechId", ocm.getString("contactMechId"), "contactMechPurposeTypeId", "BILLING_LOCATION", "userLogin", userLogin);
Map<String, Object> createBillToContactMechResult = dispatcher.runSync("createInvoiceContactMech", createBillToContactMechContext);
if (ServiceUtil.isError(createBillToContactMechResult)) {
return ServiceUtil.returnError(UtilProperties.getMessage(resource, "AccountingErrorCreatingInvoiceContactMechFromOrder", locale), null, null, createBillToContactMechResult);
}
}
} else {
Debug.logWarning("No billing locations found for order [" + orderId + "] and none were created for Invoice [" + invoiceId + "]", module);
}
}
// get a list of the payment method types
// DEJ20050705 doesn't appear to be used: List paymentPreferences = orderHeader.getRelated("OrderPaymentPreference", null, null, false);
// create the bill-from (or pay-to) contact mech as the primary PAYMENT_LOCATION of the party from the store
GenericValue payToAddress = null;
if ("PURCHASE_INVOICE".equals(invoiceType)) {
// for purchase orders, the pay to address is the BILLING_LOCATION of the vendor
GenericValue billFromVendor = orh.getPartyFromRole("BILL_FROM_VENDOR");
if (billFromVendor != null) {
List<GenericValue> billingContactMechs = billFromVendor.getRelatedOne("Party", false).getRelated("PartyContactMechPurpose", UtilMisc.toMap("contactMechPurposeTypeId", "BILLING_LOCATION"), null, false);
if (UtilValidate.isNotEmpty(billingContactMechs)) {
payToAddress = EntityUtil.getFirst(EntityUtil.filterByDate(billingContactMechs));
}
}
} else {
// for sales orders, it is the payment address on file for the store
payToAddress = PaymentWorker.getPaymentAddress(delegator, productStore.getString("payToPartyId"));
}
if (payToAddress != null) {
Map<String, Object> createPayToContactMechContext = UtilMisc.toMap("invoiceId", invoiceId, "contactMechId", payToAddress.getString("contactMechId"), "contactMechPurposeTypeId", "PAYMENT_LOCATION", "userLogin", userLogin);
Map<String, Object> createPayToContactMechResult = dispatcher.runSync("createInvoiceContactMech", createPayToContactMechContext);
if (ServiceUtil.isError(createPayToContactMechResult)) {
return ServiceUtil.returnError(UtilProperties.getMessage(resource, "AccountingErrorCreatingInvoiceContactMechFromOrder", locale), null, null, createPayToContactMechResult);
}
}
// sequence for items - all OrderItems or InventoryReservations + all Adjustments
int invoiceItemSeqNum = 1;
String invoiceItemSeqId = UtilFormatOut.formatPaddedNumber(invoiceItemSeqNum, INVOICE_ITEM_SEQUENCE_ID_DIGITS);
// create the item records
for (GenericValue currentValue : billItems) {
GenericValue itemIssuance = null;
GenericValue orderItem = null;
GenericValue shipmentReceipt = null;
if ("ItemIssuance".equals(currentValue.getEntityName())) {
itemIssuance = currentValue;
} else if ("OrderItem".equals(currentValue.getEntityName())) {
orderItem = currentValue;
} else if ("ShipmentReceipt".equals(currentValue.getEntityName())) {
shipmentReceipt = currentValue;
} else {
Debug.logError("Unexpected entity " + currentValue + " of type " + currentValue.getEntityName(), module);
}
if (orderItem == null && itemIssuance != null) {
orderItem = itemIssuance.getRelatedOne("OrderItem", false);
} else if ((orderItem == null) && (shipmentReceipt != null)) {
orderItem = shipmentReceipt.getRelatedOne("OrderItem", false);
}
if (orderItem == null) {
Debug.logError("Cannot create invoice when orderItem, itemIssuance, and shipmentReceipt are all null", module);
return ServiceUtil.returnError(UtilProperties.getMessage(resource, "AccountingIllegalValuesPassedToCreateInvoiceService", locale));
}
GenericValue product = null;
if (orderItem.get("productId") != null) {
product = orderItem.getRelatedOne("Product", false);
}
// get some quantities
BigDecimal billingQuantity = null;
if (itemIssuance != null) {
billingQuantity = itemIssuance.getBigDecimal("quantity");
BigDecimal cancelQty = itemIssuance.getBigDecimal("cancelQuantity");
if (cancelQty == null) {
cancelQty = BigDecimal.ZERO;
}
billingQuantity = billingQuantity.subtract(cancelQty).setScale(DECIMALS, ROUNDING);
} else if (shipmentReceipt != null) {
billingQuantity = shipmentReceipt.getBigDecimal("quantityAccepted");
} else {
BigDecimal orderedQuantity = OrderReadHelper.getOrderItemQuantity(orderItem);
BigDecimal invoicedQuantity = OrderReadHelper.getOrderItemInvoicedQuantity(orderItem);
billingQuantity = orderedQuantity.subtract(invoicedQuantity);
if (billingQuantity.compareTo(BigDecimal.ZERO) < 0) {
billingQuantity = BigDecimal.ZERO;
}
}
if (billingQuantity == null) {
billingQuantity = BigDecimal.ZERO;
}
// check if shipping applies to this item. Shipping is calculated for sales invoices, not purchase invoices.
boolean shippingApplies = false;
if ((product != null) && (ProductWorker.shippingApplies(product)) && ("SALES_INVOICE".equals(invoiceType))) {
shippingApplies = true;
}
BigDecimal billingAmount = BigDecimal.ZERO;
GenericValue OrderAdjustment = EntityUtil.getFirst(orderItem.getRelated("OrderAdjustment", UtilMisc.toMap("orderAdjustmentTypeId", "VAT_TAX"), null, false));
/* Apply formula to get actual product price to set amount in invoice item
Formula is: productPrice = (productPriceWithTax.multiply(100)) / (orderAdj sourcePercentage + 100))
product price = (43*100) / (20+100) = 35.83 (Here product price is 43 with VAT)
*/
if (UtilValidate.isNotEmpty(OrderAdjustment) && (OrderAdjustment.getBigDecimal("amount").signum() == 0) && UtilValidate.isNotEmpty(OrderAdjustment.getBigDecimal("amountAlreadyIncluded")) && OrderAdjustment.getBigDecimal("amountAlreadyIncluded").signum() != 0) {
BigDecimal sourcePercentageTotal = OrderAdjustment.getBigDecimal("sourcePercentage").add(new BigDecimal(100));
billingAmount = orderItem.getBigDecimal("unitPrice").divide(sourcePercentageTotal, 100, ROUNDING).multiply(new BigDecimal(100)).setScale(invoiceTypeDecimals, ROUNDING);
} else {
billingAmount = orderItem.getBigDecimal("unitPrice").setScale(invoiceTypeDecimals, ROUNDING);
}
Map<String, Object> createInvoiceItemContext = new HashMap<>();
createInvoiceItemContext.put("invoiceId", invoiceId);
createInvoiceItemContext.put("invoiceItemSeqId", invoiceItemSeqId);
createInvoiceItemContext.put("invoiceItemTypeId", getInvoiceItemType(delegator, (orderItem.getString("orderItemTypeId")), (product == null ? null : product.getString("productTypeId")), invoiceType, "INV_FPROD_ITEM"));
createInvoiceItemContext.put("description", orderItem.get("itemDescription"));
createInvoiceItemContext.put("quantity", billingQuantity);
createInvoiceItemContext.put("amount", billingAmount);
createInvoiceItemContext.put("productId", orderItem.get("productId"));
createInvoiceItemContext.put("productFeatureId", orderItem.get("productFeatureId"));
createInvoiceItemContext.put("overrideGlAccountId", orderItem.get("overrideGlAccountId"));
createInvoiceItemContext.put("userLogin", userLogin);
String itemIssuanceId = null;
if (itemIssuance != null && itemIssuance.get("inventoryItemId") != null) {
itemIssuanceId = itemIssuance.getString("itemIssuanceId");
createInvoiceItemContext.put("inventoryItemId", itemIssuance.get("inventoryItemId"));
}
// similarly, tax only for purchase invoices
if ((product != null) && ("SALES_INVOICE".equals(invoiceType))) {
createInvoiceItemContext.put("taxableFlag", product.get("taxable"));
}
Map<String, Object> createInvoiceItemResult = dispatcher.runSync("createInvoiceItem", createInvoiceItemContext);
if (ServiceUtil.isError(createInvoiceItemResult)) {
return ServiceUtil.returnError(UtilProperties.getMessage(resource, "AccountingErrorCreatingInvoiceItemFromOrder", locale), null, null, createInvoiceItemResult);
}
// this item total
BigDecimal thisAmount = billingAmount.multiply(billingQuantity).setScale(invoiceTypeDecimals, ROUNDING);
// add to the ship amount only if it applies to this item
if (shippingApplies) {
invoiceShipProRateAmount = invoiceShipProRateAmount.add(thisAmount).setScale(invoiceTypeDecimals, ROUNDING);
invoiceShippableQuantity = invoiceQuantity.add(billingQuantity).setScale(invoiceTypeDecimals, ROUNDING);
}
// increment the invoice subtotal
invoiceSubTotal = invoiceSubTotal.add(thisAmount).setScale(100, ROUNDING);
// increment the invoice quantity
invoiceQuantity = invoiceQuantity.add(billingQuantity).setScale(invoiceTypeDecimals, ROUNDING);
// create the OrderItemBilling record
Map<String, Object> createOrderItemBillingContext = new HashMap<>();
createOrderItemBillingContext.put("invoiceId", invoiceId);
createOrderItemBillingContext.put("invoiceItemSeqId", invoiceItemSeqId);
createOrderItemBillingContext.put("orderId", orderItem.get("orderId"));
createOrderItemBillingContext.put("orderItemSeqId", orderItem.get("orderItemSeqId"));
createOrderItemBillingContext.put("itemIssuanceId", itemIssuanceId);
createOrderItemBillingContext.put("quantity", billingQuantity);
createOrderItemBillingContext.put("amount", billingAmount);
createOrderItemBillingContext.put("userLogin", userLogin);
if ((shipmentReceipt != null) && (shipmentReceipt.getString("receiptId") != null)) {
createOrderItemBillingContext.put("shipmentReceiptId", shipmentReceipt.getString("receiptId"));
}
Map<String, Object> createOrderItemBillingResult = dispatcher.runSync("createOrderItemBilling", createOrderItemBillingContext);
if (ServiceUtil.isError(createOrderItemBillingResult)) {
return ServiceUtil.returnError(UtilProperties.getMessage(resource, "AccountingErrorCreatingOrderItemBillingFromOrder", locale), null, null, createOrderItemBillingResult);
}
if ("ItemIssuance".equals(currentValue.getEntityName())) {
/* Find ShipmentItemBilling based on shipmentId, shipmentItemSeqId, invoiceId, invoiceItemSeqId as
because if any order item has multiple quantity and reserved by multiple inventories then there will be multiple invoice items.
In that case ShipmentItemBilling was creating only for one invoice item. Fixed under OFBIZ-6806.
*/
List<GenericValue> shipmentItemBillings = EntityQuery.use(delegator).from("ShipmentItemBilling").where("shipmentId", currentValue.get("shipmentId"), "shipmentItemSeqId", currentValue.get("shipmentItemSeqId"), "invoiceId", invoiceId, "invoiceItemSeqId", invoiceItemSeqId).queryList();
if (UtilValidate.isEmpty(shipmentItemBillings)) {
// create the ShipmentItemBilling record
Map<String, Object> shipmentItemBillingCtx = new HashMap<>();
shipmentItemBillingCtx.put("invoiceId", invoiceId);
shipmentItemBillingCtx.put("invoiceItemSeqId", invoiceItemSeqId);
shipmentItemBillingCtx.put("shipmentId", currentValue.get("shipmentId"));
shipmentItemBillingCtx.put("shipmentItemSeqId", currentValue.get("shipmentItemSeqId"));
shipmentItemBillingCtx.put("userLogin", userLogin);
Map<String, Object> result = dispatcher.runSync("createShipmentItemBilling", shipmentItemBillingCtx);
if (ServiceUtil.isError(result)) {
return ServiceUtil.returnError(ServiceUtil.getErrorMessage(result));
}
}
}
String parentInvoiceItemSeqId = invoiceItemSeqId;
// increment the counter
invoiceItemSeqNum++;
invoiceItemSeqId = UtilFormatOut.formatPaddedNumber(invoiceItemSeqNum, INVOICE_ITEM_SEQUENCE_ID_DIGITS);
// Get the original order item from the DB, in case the quantity has been overridden
GenericValue originalOrderItem = EntityQuery.use(delegator).from("OrderItem").where("orderId", orderId, "orderItemSeqId", orderItem.get("orderItemSeqId")).queryOne();
// create the item adjustment as line items
List<GenericValue> itemAdjustments = OrderReadHelper.getOrderItemAdjustmentList(orderItem, orh.getAdjustments());
for (GenericValue adj : itemAdjustments) {
// Check against OrderAdjustmentBilling to see how much of this adjustment has already been invoiced
BigDecimal adjAlreadyInvoicedAmount = null;
try {
Map<String, Object> checkResult = dispatcher.runSync("calculateInvoicedAdjustmentTotal", UtilMisc.toMap("orderAdjustment", adj));
if (ServiceUtil.isError(checkResult)) {
Debug.logError("Accounting trouble calling calculateInvoicedAdjustmentTotal service", module);
return ServiceUtil.returnError(UtilProperties.getMessage(resource, "AccountingTroubleCallingCalculateInvoicedAdjustmentTotalService", locale));
}
adjAlreadyInvoicedAmount = (BigDecimal) checkResult.get("invoicedTotal");
} catch (GenericServiceException e) {
Debug.logError(e, "Accounting trouble calling calculateInvoicedAdjustmentTotal service", module);
return ServiceUtil.returnError(UtilProperties.getMessage(resource, "AccountingTroubleCallingCalculateInvoicedAdjustmentTotalService", locale));
}
// Set adjustment amount as amountAlreadyIncluded to continue invoice item creation process
Boolean isTaxIncludedInPrice = "VAT_TAX".equals(adj.getString("orderAdjustmentTypeId")) && UtilValidate.isNotEmpty(adj.getBigDecimal("amountAlreadyIncluded")) && adj.getBigDecimal("amountAlreadyIncluded").signum() != 0;
if ((adj.getBigDecimal("amount").signum() == 0) && isTaxIncludedInPrice) {
adj.set("amount", adj.getBigDecimal("amountAlreadyIncluded"));
}
// If the absolute invoiced amount >= the abs of the adjustment amount, the full amount has already been invoiced, so skip this adjustment
if (adjAlreadyInvoicedAmount.abs().compareTo(adj.getBigDecimal("amount").setScale(invoiceTypeDecimals, ROUNDING).abs()) > 0) {
continue;
}
BigDecimal originalOrderItemQuantity = OrderReadHelper.getOrderItemQuantity(originalOrderItem);
BigDecimal amount = BigDecimal.ZERO;
if (originalOrderItemQuantity.signum() != 0) {
if (adj.get("amount") != null) {
if ("PROMOTION_ADJUSTMENT".equals(adj.getString("orderAdjustmentTypeId")) && adj.get("productPromoId") != null) {
/* Find negative amountAlreadyIncluded in OrderAdjustment to subtract it from discounted amount.
As we stored negative sales tax amount in order adjustment for discounted item.
*/
List<EntityExpr> exprs = UtilMisc.toList(EntityCondition.makeCondition("orderId", EntityOperator.EQUALS, orderItem.getString("orderId")), EntityCondition.makeCondition("orderItemSeqId", EntityOperator.EQUALS, orderItem.getString("orderItemSeqId")), EntityCondition.makeCondition("orderAdjustmentTypeId", EntityOperator.EQUALS, "VAT_TAX"), EntityCondition.makeCondition("amountAlreadyIncluded", EntityOperator.LESS_THAN, BigDecimal.ZERO));
EntityCondition andCondition = EntityCondition.makeCondition(exprs, EntityOperator.AND);
GenericValue orderAdjustment = EntityUtil.getFirst(delegator.findList("OrderAdjustment", andCondition, null, null, null, false));
if (UtilValidate.isNotEmpty(orderAdjustment)) {
amount = adj.getBigDecimal("amount").subtract(orderAdjustment.getBigDecimal("amountAlreadyIncluded")).setScale(100, ROUNDING);
} else {
amount = adj.getBigDecimal("amount");
}
} else {
// set decimals = 100 means we don't round this intermediate value, which is very important
if (isTaxIncludedInPrice) {
BigDecimal priceWithTax = originalOrderItem.getBigDecimal("unitPrice");
// Get tax included in item price
amount = priceWithTax.subtract(billingAmount);
amount = amount.multiply(billingQuantity);
// get adjustment amount
/* Get tax amount of other invoice and calculate remaining amount need to store in invoice item(Handle case of of partial shipment and promotional item)
to adjust tax amount in invoice item.
*/
BigDecimal otherInvoiceTaxAmount = BigDecimal.ZERO;
GenericValue orderAdjBilling = EntityQuery.use(delegator).from("OrderAdjustmentBilling").where("orderAdjustmentId", adj.getString("orderAdjustmentId")).queryFirst();
if (UtilValidate.isNotEmpty(orderAdjBilling)) {
// FIXME: Need to check here isTaxIncludedInPrice pass to use cache
List<GenericValue> invoiceItems = EntityQuery.use(delegator).from("InvoiceItem").where("invoiceId", orderAdjBilling.getString("invoiceId"), "invoiceItemTypeId", "ITM_SALES_TAX", "productId", originalOrderItem.getString("productId")).cache(isTaxIncludedInPrice).queryList();
for (GenericValue invoiceItem : invoiceItems) {
otherInvoiceTaxAmount = otherInvoiceTaxAmount.add(invoiceItem.getBigDecimal("amount"));
}
if (otherInvoiceTaxAmount.compareTo(BigDecimal.ZERO) > 0) {
BigDecimal remainingAmount = adj.getBigDecimal("amountAlreadyIncluded").subtract(otherInvoiceTaxAmount);
amount = amount.min(remainingAmount);
}
}
amount = amount.min(adj.getBigDecimal("amountAlreadyIncluded")).setScale(100, ROUNDING);
} else {
amount = adj.getBigDecimal("amount").divide(originalOrderItemQuantity, 100, ROUNDING);
amount = amount.multiply(billingQuantity);
}
}
// Tax needs to be rounded differently from other order adjustments
if ("SALES_TAX".equals(adj.getString("orderAdjustmentTypeId"))) {
amount = amount.setScale(TAX_DECIMALS, TAX_ROUNDING);
} else {
amount = amount.setScale(invoiceTypeDecimals, ROUNDING);
}
} else if (adj.get("sourcePercentage") != null) {
// pro-rate the amount
// set decimals = 100 means we don't round this intermediate value, which is very important
BigDecimal percent = adj.getBigDecimal("sourcePercentage");
percent = percent.divide(new BigDecimal(100), 100, ROUNDING);
amount = billingAmount.multiply(percent);
amount = amount.divide(originalOrderItemQuantity, 100, ROUNDING);
amount = amount.multiply(billingQuantity);
amount = amount.setScale(invoiceTypeDecimals, ROUNDING);
}
}
if (amount.signum() != 0) {
Map<String, Object> createInvoiceItemAdjContext = new HashMap<>();
createInvoiceItemAdjContext.put("invoiceId", invoiceId);
createInvoiceItemAdjContext.put("invoiceItemSeqId", invoiceItemSeqId);
createInvoiceItemAdjContext.put("invoiceItemTypeId", getInvoiceItemType(delegator, adj.getString("orderAdjustmentTypeId"), null, invoiceType, "INVOICE_ITM_ADJ"));
createInvoiceItemAdjContext.put("quantity", BigDecimal.ONE);
createInvoiceItemAdjContext.put("amount", amount);
createInvoiceItemAdjContext.put("productId", orderItem.get("productId"));
createInvoiceItemAdjContext.put("productFeatureId", orderItem.get("productFeatureId"));
createInvoiceItemAdjContext.put("overrideGlAccountId", adj.get("overrideGlAccountId"));
createInvoiceItemAdjContext.put("parentInvoiceId", invoiceId);
createInvoiceItemAdjContext.put("parentInvoiceItemSeqId", parentInvoiceItemSeqId);
createInvoiceItemAdjContext.put("userLogin", userLogin);
createInvoiceItemAdjContext.put("taxAuthPartyId", adj.get("taxAuthPartyId"));
createInvoiceItemAdjContext.put("taxAuthGeoId", adj.get("taxAuthGeoId"));
createInvoiceItemAdjContext.put("taxAuthorityRateSeqId", adj.get("taxAuthorityRateSeqId"));
// some adjustments fill out the comments field instead
String description = (UtilValidate.isEmpty(adj.getString("description")) ? adj.getString("comments") : adj.getString("description"));
createInvoiceItemAdjContext.put("description", description);
// TODO: This is not an ideal solution. Instead, we need to use OrderAdjustment.includeInTax when it is implemented
if (!("SALES_TAX".equals(adj.getString("orderAdjustmentTypeId")))) {
createInvoiceItemAdjContext.put("taxableFlag", product.get("taxable"));
}
// represent an organization override for the payToPartyId
if (UtilValidate.isNotEmpty(adj.getString("productPromoId"))) {
try {
GenericValue productPromo = adj.getRelatedOne("ProductPromo", false);
if (UtilValidate.isNotEmpty(productPromo.getString("overrideOrgPartyId"))) {
createInvoiceItemAdjContext.put("overrideOrgPartyId", productPromo.getString("overrideOrgPartyId"));
}
} catch (GenericEntityException e) {
Debug.logError(e, "Error looking up ProductPromo with id [" + adj.getString("productPromoId") + "]", module);
}
}
Map<String, Object> createInvoiceItemAdjResult = dispatcher.runSync("createInvoiceItem", createInvoiceItemAdjContext);
if (ServiceUtil.isError(createInvoiceItemAdjResult)) {
return ServiceUtil.returnError(UtilProperties.getMessage(resource, "AccountingErrorCreatingInvoiceItemFromOrder", locale), null, null, createInvoiceItemAdjResult);
}
// Create the OrderAdjustmentBilling record
Map<String, Object> createOrderAdjustmentBillingContext = new HashMap<>();
createOrderAdjustmentBillingContext.put("orderAdjustmentId", adj.getString("orderAdjustmentId"));
createOrderAdjustmentBillingContext.put("invoiceId", invoiceId);
createOrderAdjustmentBillingContext.put("invoiceItemSeqId", invoiceItemSeqId);
createOrderAdjustmentBillingContext.put("amount", amount);
createOrderAdjustmentBillingContext.put("userLogin", userLogin);
Map<String, Object> createOrderAdjustmentBillingResult = dispatcher.runSync("createOrderAdjustmentBilling", createOrderAdjustmentBillingContext);
if (ServiceUtil.isError(createOrderAdjustmentBillingResult)) {
return ServiceUtil.returnError(UtilProperties.getMessage(resource, "AccountingErrorCreatingOrderAdjustmentBillingFromOrder", locale), null, null, createOrderAdjustmentBillingContext);
}
// this adjustment amount
BigDecimal thisAdjAmount = amount;
// adjustments only apply to totals when they are not tax or shipping adjustments
if (!"SALES_TAX".equals(adj.getString("orderAdjustmentTypeId")) && !"SHIPPING_ADJUSTMENT".equals(adj.getString("orderAdjustmentTypeId"))) {
// increment the invoice subtotal
invoiceSubTotal = invoiceSubTotal.add(thisAdjAmount).setScale(100, ROUNDING);
// add to the ship amount only if it applies to this item
if (shippingApplies) {
invoiceShipProRateAmount = invoiceShipProRateAmount.add(thisAdjAmount).setScale(invoiceTypeDecimals, ROUNDING);
}
}
// increment the counter
invoiceItemSeqNum++;
invoiceItemSeqId = UtilFormatOut.formatPaddedNumber(invoiceItemSeqNum, INVOICE_ITEM_SEQUENCE_ID_DIGITS);
}
}
}
// create header adjustments as line items -- always to tax/shipping last
Map<GenericValue, BigDecimal> shipAdjustments = new HashMap<>();
Map<GenericValue, BigDecimal> taxAdjustments = new HashMap<>();
List<GenericValue> headerAdjustments = orh.getOrderHeaderAdjustments();
for (GenericValue adj : headerAdjustments) {
// Check against OrderAdjustmentBilling to see how much of this adjustment has already been invoiced
BigDecimal adjAlreadyInvoicedAmount = null;
try {
Map<String, Object> checkResult = dispatcher.runSync("calculateInvoicedAdjustmentTotal", UtilMisc.toMap("orderAdjustment", adj));
if (ServiceUtil.isError(checkResult)) {
return ServiceUtil.returnError(UtilProperties.getMessage(resource, "AccountingTroubleCallingCalculateInvoicedAdjustmentTotalService", locale));
}
adjAlreadyInvoicedAmount = ((BigDecimal) checkResult.get("invoicedTotal")).setScale(invoiceTypeDecimals, ROUNDING);
} catch (GenericServiceException e) {
Debug.logError(e, "Accounting trouble calling calculateInvoicedAdjustmentTotal service", module);
return ServiceUtil.returnError(UtilProperties.getMessage(resource, "AccountingTroubleCallingCalculateInvoicedAdjustmentTotalService", locale));
}
// If the absolute invoiced amount >= the abs of the adjustment amount, the full amount has already been invoiced, so skip this adjustment
if (adjAlreadyInvoicedAmount.abs().compareTo(adj.getBigDecimal("amount").setScale(invoiceTypeDecimals, ROUNDING).abs()) >= 0) {
continue;
}
if ("SHIPPING_CHARGES".equals(adj.getString("orderAdjustmentTypeId"))) {
shipAdjustments.put(adj, adjAlreadyInvoicedAmount);
} else if ("SALES_TAX".equals(adj.getString("orderAdjustmentTypeId"))) {
taxAdjustments.put(adj, adjAlreadyInvoicedAmount);
} else {
// these will effect the shipping pro-rate (unless commented)
// other adjustment type
BigDecimal divisor = orderSubTotal;
BigDecimal multiplier = invoiceSubTotal;
if (BigDecimal.ZERO.compareTo(multiplier) == 0 && BigDecimal.ZERO.compareTo(divisor) == 0) {
// if multiplier and divisor are equal to zero then use the quantities instead of the amounts
// this is useful when the order has free items and misc charges
divisor = orderQuantity;
multiplier = invoiceQuantity;
}
calcHeaderAdj(delegator, adj, invoiceType, invoiceId, invoiceItemSeqId, divisor, multiplier, adj.getBigDecimal("amount").setScale(invoiceTypeDecimals, ROUNDING), invoiceTypeDecimals, ROUNDING, userLogin, dispatcher, locale);
// invoiceShipProRateAmount += adjAmount;
// do adjustments compound or are they based off subtotal? Here we will (unless commented)
// invoiceSubTotal += adjAmount;
// increment the counter
invoiceItemSeqNum++;
invoiceItemSeqId = UtilFormatOut.formatPaddedNumber(invoiceItemSeqNum, INVOICE_ITEM_SEQUENCE_ID_DIGITS);
}
}
// numerator/denominator problems when the shipping is not pro-rated but rather charged all on the first invoice
for (Map.Entry<GenericValue, BigDecimal> set : shipAdjustments.entrySet()) {
BigDecimal adjAlreadyInvoicedAmount = set.getValue();
GenericValue adj = set.getKey();
if ("N".equalsIgnoreCase(prorateShipping)) {
// Set the divisor and multiplier to 1 to avoid prorating
BigDecimal divisor = BigDecimal.ONE;
BigDecimal multiplier = BigDecimal.ONE;
// The base amount in this case is the adjustment amount minus the total already invoiced for that adjustment, since
// it won't be prorated
BigDecimal baseAmount = adj.getBigDecimal("amount").setScale(invoiceTypeDecimals, ROUNDING).subtract(adjAlreadyInvoicedAmount);
calcHeaderAdj(delegator, adj, invoiceType, invoiceId, invoiceItemSeqId, divisor, multiplier, baseAmount, invoiceTypeDecimals, ROUNDING, userLogin, dispatcher, locale);
} else {
// Pro-rate the shipping amount based on shippable information
BigDecimal divisor = shippableAmount;
BigDecimal multiplier = invoiceShipProRateAmount;
if (BigDecimal.ZERO.compareTo(multiplier) == 0 && BigDecimal.ZERO.compareTo(divisor) == 0) {
// if multiplier and divisor are equal to zero then use the quantities instead of the amounts
// this is useful when the order has free items and shipping charges
divisor = shippableQuantity;
multiplier = invoiceShippableQuantity;
}
// The base amount in this case is the adjustment amount, since we want to prorate based on the full amount
BigDecimal baseAmount = adj.getBigDecimal("amount").setScale(invoiceTypeDecimals, ROUNDING);
calcHeaderAdj(delegator, adj, invoiceType, invoiceId, invoiceItemSeqId, divisor, multiplier, baseAmount, invoiceTypeDecimals, ROUNDING, userLogin, dispatcher, locale);
}
// Increment the counter
invoiceItemSeqNum++;
invoiceItemSeqId = UtilFormatOut.formatPaddedNumber(invoiceItemSeqNum, INVOICE_ITEM_SEQUENCE_ID_DIGITS);
}
// last do the tax adjustments
String prorateTaxes = productStore != null ? productStore.getString("prorateTaxes") : "Y";
if (prorateTaxes == null) {
prorateTaxes = "Y";
}
for (Map.Entry<GenericValue, BigDecimal> entry : taxAdjustments.entrySet()) {
GenericValue adj = entry.getKey();
BigDecimal adjAlreadyInvoicedAmount = entry.getValue();
BigDecimal adjAmount = null;
if ("N".equalsIgnoreCase(prorateTaxes)) {
// Set the divisor and multiplier to 1 to avoid prorating
BigDecimal divisor = BigDecimal.ONE;
BigDecimal multiplier = BigDecimal.ONE;
// The base amount in this case is the adjustment amount minus the total already invoiced for that adjustment, since
// it won't be prorated
BigDecimal baseAmount = adj.getBigDecimal("amount").setScale(TAX_DECIMALS, TAX_ROUNDING).subtract(adjAlreadyInvoicedAmount);
adjAmount = calcHeaderAdj(delegator, adj, invoiceType, invoiceId, invoiceItemSeqId, divisor, multiplier, baseAmount, TAX_DECIMALS, TAX_ROUNDING, userLogin, dispatcher, locale);
} else {
// Pro-rate the tax amount based on shippable information
BigDecimal divisor = orderSubTotal;
BigDecimal multiplier = invoiceSubTotal;
// The base amount in this case is the adjustment amount, since we want to prorate based on the full amount
BigDecimal baseAmount = adj.getBigDecimal("amount");
adjAmount = calcHeaderAdj(delegator, adj, invoiceType, invoiceId, invoiceItemSeqId, divisor, multiplier, baseAmount, TAX_DECIMALS, TAX_ROUNDING, userLogin, dispatcher, locale);
}
invoiceSubTotal = invoiceSubTotal.add(adjAmount).setScale(invoiceTypeDecimals, ROUNDING);
// Increment the counter
invoiceItemSeqNum++;
invoiceItemSeqId = UtilFormatOut.formatPaddedNumber(invoiceItemSeqNum, INVOICE_ITEM_SEQUENCE_ID_DIGITS);
}
// check for previous order payments
List<GenericValue> orderPaymentPrefs = EntityQuery.use(delegator).from("OrderPaymentPreference").where(EntityCondition.makeCondition("orderId", EntityOperator.EQUALS, orderId), EntityCondition.makeCondition("statusId", EntityOperator.NOT_EQUAL, "PAYMENT_CANCELLED")).queryList();
List<GenericValue> currentPayments = new LinkedList<>();
for (GenericValue paymentPref : orderPaymentPrefs) {
List<GenericValue> payments = paymentPref.getRelated("Payment", null, null, false);
currentPayments.addAll(payments);
}
// apply these payments to the invoice if they have any remaining amount to apply
for (GenericValue payment : currentPayments) {
if ("PMNT_VOID".equals(payment.getString("statusId")) || "PMNT_CANCELLED".equals(payment.getString("statusId"))) {
continue;
}
BigDecimal notApplied = PaymentWorker.getPaymentNotApplied(payment);
if (notApplied.signum() > 0) {
Map<String, Object> appl = new HashMap<>();
appl.put("paymentId", payment.get("paymentId"));
appl.put("invoiceId", invoiceId);
appl.put("billingAccountId", billingAccountId);
appl.put("amountApplied", notApplied);
appl.put("userLogin", userLogin);
Map<String, Object> createPayApplResult = dispatcher.runSync("createPaymentApplication", appl);
if (ServiceUtil.isError(createPayApplResult)) {
return ServiceUtil.returnError(UtilProperties.getMessage(resource, "AccountingErrorCreatingInvoiceFromOrder", locale), null, null, createPayApplResult);
}
}
}
// Should all be in place now. Depending on the ProductStore.autoApproveInvoice setting, set status to INVOICE_READY (unless it's a purchase invoice, which we set to INVOICE_IN_PROCESS)
String autoApproveInvoice = productStore != null ? productStore.getString("autoApproveInvoice") : "Y";
if (!"N".equals(autoApproveInvoice)) {
String nextStatusId = "PURCHASE_INVOICE".equals(invoiceType) ? "INVOICE_IN_PROCESS" : "INVOICE_READY";
Map<String, Object> setInvoiceStatusResult = dispatcher.runSync("setInvoiceStatus", UtilMisc.<String, Object>toMap("invoiceId", invoiceId, "statusId", nextStatusId, "userLogin", userLogin));
if (ServiceUtil.isError(setInvoiceStatusResult)) {
return ServiceUtil.returnError(UtilProperties.getMessage(resource, "AccountingErrorCreatingInvoiceFromOrder", locale), null, null, setInvoiceStatusResult);
}
}
Map<String, Object> resp = ServiceUtil.returnSuccess();
resp.put("invoiceId", invoiceId);
resp.put("invoiceTypeId", invoiceType);
return resp;
} catch (GenericEntityException e) {
Debug.logError(e, "Entity/data problem creating invoice from order items: " + e.toString(), module);
return ServiceUtil.returnError(UtilProperties.getMessage(resource, "AccountingEntityDataProblemCreatingInvoiceFromOrderItems", UtilMisc.toMap("reason", e.toString()), locale));
} catch (GenericServiceException e) {
Debug.logError(e, "Service/other problem creating invoice from order items: " + e.toString(), module);
return ServiceUtil.returnError(UtilProperties.getMessage(resource, "AccountingServiceOtherProblemCreatingInvoiceFromOrderItems", UtilMisc.toMap("reason", e.toString()), locale));
}
}
use of org.apache.ofbiz.entity.condition.EntityCondition in project ofbiz-framework by apache.
the class PaymentWorker method getPaymentApplied.
/**
* Method to return the total amount of a payment which is applied to a payment
* @param payment GenericValue object of the Payment
* @param actual false for currency of the payment, true for the actual currency
* @return the applied total as BigDecimal in the currency of the payment
*/
public static BigDecimal getPaymentApplied(GenericValue payment, Boolean actual) {
BigDecimal paymentApplied = BigDecimal.ZERO;
List<GenericValue> paymentApplications = null;
try {
List<EntityExpr> cond = UtilMisc.toList(EntityCondition.makeCondition("paymentId", EntityOperator.EQUALS, payment.getString("paymentId")), EntityCondition.makeCondition("toPaymentId", EntityOperator.EQUALS, payment.getString("paymentId")));
EntityCondition partyCond = EntityCondition.makeCondition(cond, EntityOperator.OR);
paymentApplications = payment.getDelegator().findList("PaymentApplication", partyCond, null, UtilMisc.toList("invoiceId", "billingAccountId"), null, false);
if (UtilValidate.isNotEmpty(paymentApplications)) {
for (GenericValue paymentApplication : paymentApplications) {
BigDecimal amountApplied = paymentApplication.getBigDecimal("amountApplied");
// check currency invoice and if different convert amount applied for display
if (actual.equals(Boolean.FALSE) && paymentApplication.get("invoiceId") != null && payment.get("actualCurrencyAmount") != null && payment.get("actualCurrencyUomId") != null) {
GenericValue invoice = paymentApplication.getRelatedOne("Invoice", false);
if (payment.getString("actualCurrencyUomId").equals(invoice.getString("currencyUomId"))) {
amountApplied = amountApplied.multiply(payment.getBigDecimal("amount")).divide(payment.getBigDecimal("actualCurrencyAmount"), new MathContext(100));
}
}
paymentApplied = paymentApplied.add(amountApplied).setScale(decimals, rounding);
}
}
} catch (GenericEntityException e) {
Debug.logError(e, "Trouble getting entities", module);
}
return paymentApplied;
}
Aggregations