Conditional parameters based on another parameter's value - apiblueprint

What is the format for parameters if I have condition where some parameters can be changed based on another param's value.
Eg: I have payment methods: paypal, bank transfer, etc.
+ amount (required, number, `1200`) ... charge amount
+ method (required, string, `paypal`) ... payment method
+ Values
+ `paypal`
+ `bank transfer`
+ `etc`
if method's value is "paypal", then my service need additional param:
+ email (required, string, `user#email.com`) ... user's paypal account
if method's value is "bank transfer", then my service need additional params:
+ bank_name (required, string, `Panama National Bank`) ... user's bank
+ bank_account (required, number, `3445315-XXX`) ... user's bank account

I got this answer from username kyle who commented on issue that opened on apiary's GitHub. Below is just copy paste from what Mr.Kyle said.
This isn't possible at the moment, but it is something on our radar and in our roadmap. We're planning to make parameters use MSON, which would allow you to use one of which should allow you to achieve this.
+ Parameters
+ amount: 1200 (number) - Charge Amount
+ One Of
+ Properties
+ method: paypal (required, fixed)
+ email (required)
+ Properties
+ method: bank (required, fixed)
+ `bank_name` (required)
+ `bank_account` (required)

Related

By usecases 1.create customer 2.update(kyc) 3.Deposit 4 Fund transfer using 3 microservices Api validation

USE case :
Create customer
create a table with name customers having following columns : Id(Numeric), customer_id(varchar), customer_name(varchar), customer_mobileNo(Numeric), EmailId(Varchar) Account_Id(varchar), Amount(Numeric), address(varchar), is_KYC_DONE(Boolean), active(Boolean)
Make a post rest end point to insert data into the customer table and register 2 customers with default amount like 0.
API Validation be like customer_id, customer_name, account_id should be mandatory, Mobile number should be numeric, Email should be in right format, is_KYC_DONE and active should have default value of 0 and 1
Update KYC to true
Make a patch rest point to update kyc in table to true on basis of customer id.
Deposit
Make a post rest end point to deposit amount in above created table based on account id
Deposit should happen when kyc is done i.e true otherwise tranaction will be declined
Fund transfer
Make a post rest point to do amount transfer from one account to another.
payload should be like this :
transfer {
“Amount” :
withdraw {
“Account_id” : “”
},
Deposit {
“Account_id” : “”
}
}
Validation be like
Sufficent funds should be dere
for both withdraw and deposit accountId and amount should be mandatory.
if deposit fails then amount withdrawn from account should be rollbacked.
NOTE : Mke sure atmost once and at least once the transaction should happen.
There will 3 microservices
customer : will have create customer and update kyc end points
Deposit : will have deposit end point
fundtransfer : will have fundtransfer endpoint, withdraw happens from this microservice and for deposit call should go to deposit miroservice.
I am in the process of learning about microservices and there's one thing that I can't seem to figure out and I can't find any resources that give me a direct answer to this. The question is: Do microservices involve only business logic and

Prevent Available stock become minus

It's possible that in Adempiere, Inventory Stock become negative. One of the way to make it become negative is when we put Quantity in Internal Use Inventory more than Available Stock in Warehouse.
Example
Product Info
------------
Product || Qty
Fertilizer || 15
It's shown in Product Info that the current Qty of Fertilizer is 15. Then I make Internal Use Inventory document
Internal Use Inventory
----------------------
Product || Qty
Fertilizer || 25
When I complete it, Quantity will be -10. How can I prevent Internal Use Inventory being completed when Quantity is more than Available Stock so that I can avoid negative stock ?
This is purposely designed functionality of Adempiere. Under some scenarios the Inventory is allowed to become negative because it is felt in those scenarios it is better to allow the process to complete but being negative it highlights a problem that must be addressed. In the case of the Internal Use the user is warned, that the stock will go negative if they proceed.
To change this standard functionality you need to modify the
org.compiere.model.MInventory.completeIt()
But if you change the code directly, it will make it more difficult to keep your version in sync with the base Adempiere or even just applying patches.
The recommended approach would be to add a Model Validator. This is a mechanism that watches the underlying data model and enables additional code/logic to be injected when a specific event occurs.
The event you want is the Document Event TIMING_BEFORE_COMPLETE.
You would create a new model validator as described in the link, register it in Adempiere's Application Dictionary and since you want your code to trigger when Inventory Document Type is executed you would add a method something like this
public String docValidate (PO po, int timing)
{
if (timing == TIMING_BEFORE_COMPLETE) {
if (po.get_TableName().equals(MInventory.Table_Name))
{
// your code to be executed
// it is executed just before any (internal or physical)
// Inventory is Completed
}
}
return null;
} // docValidate
A word of warning; the Internal Use functionality is the same used by Physical Inventory (i.e. a stock count) functionality! They just have different windows in Adempiere. So be sure to test both functionalities after any change is applied. From the core org.compiere.model.MInventory there is a hint as how you might differentiate the two.
//Get Quantity Internal Use
BigDecimal qtyDiff = line.getQtyInternalUse().negate();
//If Quantity Internal Use = Zero Then Physical Inventory Else Internal Use Inventory
if (qtyDiff.signum() == 0)
In order to prevent the stock from being negative you can use two methods
Callout in Code
BeforeSave Method
In order to apply it in Callout you need to create a Callout class and get the current Stock Qty at the locator there and then subtract the qty from entered Qty and if the result is less than 0 , display the error. Apply this on the Qty field and you will get the desired result.
This is slightly better way , as this doesn't needs creating a new class in code altogether and will consume less memory altogether , Search for MInventoryLine class in code and then search for beforesave() in it. Add the same code (getting the stock and then subtracting it from the entered Qty). The Code in beforesave() will be like this
if (your condition here) { log.saveError("Could Not Save - Error", "Qty less than 0"); return false; }
Now I am assuming that you know the basic code to create a Callout and design a condition, If you need any help , let me know.
Adding to the answer of Mr. Colin, please see the below code to restrict the negative inventory from the M_Inventory based transaction. You can consider the same concept in M_Inout and M_Movement Tables also.
for (MInventoryLine line : mInventory.getLines(true))
{
String blockNegativeQty = MSysConfig.getValue("BLOCK_NEGATIVE_QUANTITY_IN_MATERIAL_ISSUE","Y", getAD_Client_ID());
if(blockNegativeQty.equals("Y"))
{
//Always check the on Hand Qty not Qty Book Value. The old Drafted Qty Book Value may be changed alredy.
String sql = "SELECT adempiere.bomqtyonhand(?, ?, ?)";
BigDecimal onhandQty = DB.getSQLValueBD(line.get_TrxName(), sql, line.getM_Product_ID(), mInventory.getM_Warehouse_ID()
, line.getM_Locator_ID());
BigDecimal QtyMove = onhandQty.subtract(line.getQtyInternalUse());
if(QtyMove.compareTo(Env.ZERO) < 0)
{
log.warning("Negative Movement Quantity is restricted. Qty On Hand = " + onhandQty
+ " AND Qty Internal Use = " + line.getQtyInternalUse()
+ ". The Movement Qty is = " + QtyMove);
negativeCount ++;
negativeProducts = negativeProducts + line.getM_Product().getName() + ": " + onhandQty + " ; ";
}
}
}
if(negativeCount > 0)
{
m_processMsg = "Negative Inventory Restricted. "
+ " Restriction has been enabled through the client level system configurator."
+ " Products : " + negativeProducts;
}
return m_processMsg;

How can I prioritize search country in Bing Maps Ajax

In current realization I just add country to query, but this logic is incorrect.
$.ajax({
url: 'https://dev.virtualearth.net/REST/v1/Locations/'
+ searchString + ' ' + currentCountry + '?c=' + currentCulture + ...,
dataType: 'script'
});
But if I try to search city out of currentCountry, I will find currentCountry, but not searchString .
Example: searchString = Boston, currentCountry = Australia
Second approach - remove currentCountry from query at all, but it will cause another bug.
I will not find less famous place in currentCountry
Example: searchString = Moscow, currentCountry = USA.
Result of this query will be - Moscow, Russia and not Moscow, Washington, D.C.
Is there some ability to specify search country?
First try of search will be in currentCountry, if it fails, then second will be in all World.
If you read the best practices for Bing Maps it actually recommends using the unstructured URL format rather than the structured format when geocoding as it will increase the accuracy of the results: https://msdn.microsoft.com/en-us/library/dn894107.aspx
In order to limit the results to a single country, simple append the country to the query.
Your code is close but there should be a comma, and you would be using the query parameter as the format you are using is meant for reverse geocoding coordinates. Here is a modified version of your code:
$.ajax({
url: 'https://dev.virtualearth.net/REST/v1/Locations?q='
+ encodeURIComponent(searchString + ', ' + currentCountry) + '?c=' + currentCulture + ...,
dataType: 'script'
});
You also want to encode the query value to ensure that special characters do not cause any issues.
According to Find a Location by Address you could get the latitude and longitude coordinates based on a set of address values for specific countries.
Example:
The query:
http://dev.virtualearth.net/REST/v1/Locations/US/Moscow?o=xml&key=BingMapsKey
Note: A structured URL specifies the location data for the country
(US in this example) as part of the URL path.
will return locations for Moscow situated in United States.

How can I make automatic generated column in Adempiere ?

I have two columns called Quantity and Issued Quantity. I want that when I put value in Quantity column, for instance 3, the Issued Quantity will automatically generate 3. Also I want it to happen the other way around.
The example is on Purchase Order window, PO Line tab. in Quantity section. When I put 4 in Quantity field, the PO Quantity field automatically generate 4.
I try to imitate the column and field but it doesn't work.
This is accomplished in Adempiere by a Callout which is configured in what Adempiere calls the Application Dictionary
From the example you gave; updating the qty on the Purchase Order.
If you login to Adempiere using the System user, you can view and modify the Application Dictionary.
From the main menu select Application Dictionary->Table & Column.
In the Search box that opens enter C_OrderLine as the DB Table name.
Now Column tab and scroll down the list to locate the QtyEntered column. Switch to the Form view and near the end you will see were you can enter the field Callout.
You should see that the C_OrderLine.QtyEntered field already has a value "org.compiere.model.CalloutOrder.qty; org.compiere.model.CalloutOrder.amt" which indicates it should run the method qty in the class org.compiere.model.CalloutOrder followed by the method amt in the org.compiere.model.CalloutOrder class.
If you open those classes you can see how easily you can evaluate and modify values. Breaking some of of it down for you... if you open the CalloutOrder.java class you cab scroll down until you find the qty method.
public String qty (Properties ctx, int WindowNo, GridTab mTab, GridField mField, Object value)
{
You need use the signature as above for any new callout method you create. Follow that approach and Adempiere will look after passing the correct values for you
if (isCalloutActive() || value == null)
return "";
It's good practice to start the method with the above ensure you do not open a Callout from within a Callout - which would break the Adempiere rules.
int M_Product_ID = Env.getContextAsInt(ctx, WindowNo, "M_Product_ID");
Is an example of how you could extract values from the existing window... the syntax would remain the same regardless of Column/Field the you just need to enter the "M_Product_ID" which is the Field name you wish to extract to use.
Now this Callout is called by more than one Column/Field so it is littered with a big if...then...else to executed the logic needed for the relevant field. It's not pretty, but this is aimed at business developers who will concentrate more on business logic than coding principals.
The code you are interested in is
else if (mField.getColumnName().equals("QtyEntered"))
{
int C_UOM_To_ID = Env.getContextAsInt(ctx, WindowNo, "C_UOM_ID");
QtyEntered = (BigDecimal)value;
BigDecimal QtyEntered1 = QtyEntered.setScale(MUOM.getPrecision(ctx, C_UOM_To_ID), BigDecimal.ROUND_HALF_UP);
if (QtyEntered.compareTo(QtyEntered1) != 0)
{
log.fine("Corrected QtyEntered Scale UOM=" + C_UOM_To_ID
+ "; QtyEntered=" + QtyEntered + "->" + QtyEntered1);
QtyEntered = QtyEntered1;
mTab.setValue("QtyEntered", QtyEntered);
}
QtyOrdered = MUOMConversion.convertProductFrom (ctx, M_Product_ID,
C_UOM_To_ID, QtyEntered);
if (QtyOrdered == null)
QtyOrdered = QtyEntered;
boolean conversion = QtyEntered.compareTo(QtyOrdered) != 0;
log.fine("UOM=" + C_UOM_To_ID
+ ", QtyEntered=" + QtyEntered
+ " -> " + conversion
+ " QtyOrdered=" + QtyOrdered);
Env.setContext(ctx, WindowNo, "UOMConversion", conversion ? "Y" : "N");
mTab.setValue("QtyOrdered", QtyOrdered);
}
The code
mTab.setValue("QtyOrdered", QtyOrdered);
Is where it sets the value of the other quantity field Qty Ordered.
Also, the call must not be Java. It is possible to link any [JSR223 script][3]. I never actually tried this approach myself, but it is even possible to implement Callouts using the Drools Rules Engine!

Account Balance For Invoice Purposes Access 2010

I have created a new transactions table (tblTransactions) where I can log all my [Debits] (i.e. invoices) and [Credits] (i.e.payments). From this I can run a query with an an expression to get the Current Account Balance.
However, for invoicing purposes, if I pulled the Current Account Balance as described above, it wouldn't be the 'previous account balance' value I want to display to the customer on the invoice, because it's showing a value which includes the current invoice amount. I want the customer to see the previous balance PRIOR TO that invoice.
My current solution uses expressions in a query to calculate this 'previous account balance', but I don't know if could be done another way. Any suggestions on how this could be done better?
I couldn't post a picture of the query b/c I don't have enough reputation points.
Invoice fields I'm trying to populate:
Invoice Amount
Previous Account Balance (prior to this invoice)
Final Invoice Amount
Below are my calculations used in the query
Invoice Amount: Debit
Previous Account Balance: Format([Account Credits]-[Account Debits],"Currency")
Final Invoice Amount: Format([Invoice Amount]-[Previous Account Balance],"Currency")
Account Debits: Format(DSum("[Debit]","tblTransactions","ClientID =" & [ClientID])-[Debit],"Currency")
*This formula takes out the value of the current debit in the query
Account Credits: Format(DSum("[Credit]","tblTransactions","ClientID =" & [ClientID]),"Currency")
A solution "quick and dirty" is to find add a condition to your query excluding the record on which you are working (current invoice).
If you have an ID, you can add a WHERE condition to your DSUM, excluding the current record.
Let's assume IDTransact is ID field of tblTransactions, your WHERE condition should be
"ClientID =" & [ClientID] & "AND IDTransact <> " & CStr([IDTransact])
I assumed the ID is a long (autonumbering).
Hope this can help you.
Let me know.
Bye

Resources