How To sum multiple fields in Acumatica (pxformula) - acumatica

I know pxformula could do it, but pxformula only accepts two argument parameters. how can i add (sum) multiple fields of the same DAC? can i nest it?
thanks. some working examples would be appreciated, some other methods would also be appreciated.

As suggested in another answer, PXFormula can be used to perform a multi field calculation. However, PXFormula always assigns calculated value to the field it decorates.
PXUnboundFormulaAttribute might be a better approach in case you don't need to store calculated value in any field:
[PXUnboundFormulaAttribute(typeof(Switch<Case<Where<GLTranDoc.debitAccountID, IsNotNull>, GLTranDoc.curyTranTotal>, Sub<GLTranDoc.curyTaxAmt, GLTranDoc.curyInclTaxAmt>>),
typeof(SumCalc<GLDocBatch.curyDebitTotal>))]
For additional examples on the PXUnboundFormulaAttribute, please check Example 7.3: Adding Conditional Calculation of Aggregated Values in the T200 developer class guide at Acumatica University or Acumatica Open University

I know this post is old, but I have been stuck on this for a while. I finally found a simple solution using [PXFormula] by nesting ADD's.
I had made a new grid screen and I wanted the final column to total all the other columns.
MY DAC’s :
one,two,three,four,five,six,seven,eight,nine,ten,eleven,twelve
The solution:
[PXFormula(typeof(Add<Add<Add<Add<Add<Add<one, two>,
Add<three, four>>, Add<five, six>>, Add<seven, eight>>,
Add<nine, ten>>, Add < eleven, twelve >>), typeof(SumCalc<total>))]
[PXDBDecimal()]
[PXUIField(DisplayName = "Total", Enabled = false)]
public virtual Decimal? Total { get; set; }
public abstract class total : PX.Data.BQL.BqlDecimal.Field<total> { }

If you do a code search on PXFormula you should find many examples. I usually search the code found in your site/App_data/CpdeRepository directory if you have access to a local site.
If you are looking to perform a multi field calculations, you nest your Add, Sub, Mult, Div, etc. calls.
Here are some examples from my search on "PXFormula" or "Mult<" or "Add<":
Found in ARTranRUTROT.CuryRUTROTTotal, this example will subtrack curyExtPrice from curyDiscAmt and add curyRUTROTTaxAmountDeductible (unless null use zero)
[PXFormula(typeof(Add<Sub<ARTran.curyExtPrice, ARTran.curyDiscAmt>,
IsNull<curyRUTROTTaxAmountDeductible, decimal0>>))]
Found in GLTaxTran.CuryExpenseAmt. This example again uses multiple fields in the calculation all nested.
[PXFormula(typeof(Mult<Mult<GLTaxTran.curyTaxableAmt,
Div<GLTaxTran.taxRate, decimal100>>, Sub<decimal1,
Div<GLTaxTran.nonDeductibleTaxRate, decimal100>>>), null)]

Related

Does jooq record use column indexes when fetching data?

I'm investigating an issue where we are seeing strange exceptions related to jooq trying to populate a generated Record class, where it gets data type errors because it uses java.sql.ResultSet::getXXX(int) (column index based) to fetch data.
The part of the stacktrace that I can share looks like:
Caused by: java.sql.SQLDataException: Value 'ABC' is outside of valid range for type java.lang.Byte
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:114)
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:97)
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:89)
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:63)
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:73)
at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:92)
at com.mysql.cj.jdbc.result.ResultSetImpl.getObject(ResultSetImpl.java:1423)
at com.mysql.cj.jdbc.result.ResultSetImpl.getByte(ResultSetImpl.java:710)
at com.zaxxer.hikari.pool.HikariProxyResultSet.getByte(HikariProxyResultSet.java)
at org.jooq.tools.jdbc.DefaultResultSet.getByte(DefaultResultSet.java:124)
at org.jooq.tools.jdbc.DefaultResultSet.getByte(DefaultResultSet.java:124)
at org.jooq.impl.CursorImpl$CursorResultSet.getByte(CursorImpl.java:688)
at org.jooq.impl.DefaultBinding$DefaultByteBinding.get0(DefaultBinding.java:1783)
at org.jooq.impl.DefaultBinding$DefaultByteBinding.get0(DefaultBinding.java:1755)
at org.jooq.impl.DefaultBinding$AbstractBinding.get(DefaultBinding.java:871)
at org.jooq.impl.CursorImpl$CursorIterator$CursorRecordInitialiser.setValue(CursorImpl.java:1725)
Which is definitely a column mismatch caused using wrong column index.
The issue comes up because we are using the record on an evolving schema so the underlying table contains columns not available in the record definition.
Note that the actual code that triggers this is:
jooq.insertInto(TABLE)
.set(TABLE.COL, "ABC")
.returning(TABLE.asterisk())
.fetchOne();
What scares me a bit here is, that if it does indeed use column indexes by design, that will make schema evolution somewhat hard (how would you delete a column from a running application).
Long story (sorry), question is: does jooq use column index in records generated by jooq-generator and is there a way to use column names instead?
One thing I have noticed is that when I compare the documentation at https://www.jooq.org/doc/3.14/manual/code-generation/codegen-records/ the shown generated records does not match what the generator actually generates. The documentation show methods like:
// Every column generates a setter and a getter
#Override
public void setId(Integer value) {
setValue(BOOK.ID, value);
}
But in reality the generated code looks like (taken from jOOQ-examples):
/**
* Setter for <code>PUBLIC.BOOK.ID</code>.
*/
public void setId(Integer value) {
set(0, value);
}
Btw we are using jooq 3.14.15.
Ok, this was a local error. What actually caused the issue was that our code was written as:
jooq.insertInto(TABLE)
.set(TABLE.COL, "ABC")
.returning(TABLE.asterisk())
.fetchOne();
And the TABLE.asterisk() is what messes it up (since on the database that contains extra columns it does not return what jooq expects). Fortunately removing it solves the problem so our code now looks like:
jooq.insertInto(TABLE)
.set(TABLE.COL, "ABC")
.returning()
.fetchOne();

Overriding second level graph extension method in Acumatica

I need to work with the reusable business objects for Sales tax, discounts, etc. and need to override some of the methods in these graph extensions. For example I am starting with the Opportunities graph. I have a set of order totals that need to calculate into the overall products amount and in the past we just overrode the tax attribute on (I think) tax category. Anyhow I don't see how its possible to use the PXOverrideAttribute on a method from a second level graph extension.
Here is my example:
public class OpportunityMaintExtOne : PXGraphExtension<PX.Objects.CR.OpportunityMaint.SalesTax, PX.Objects.CR.OpportunityMaint>
{
[PXOverride]
public virtual void CalcDocTotals(object row, decimal CuryTaxTotal, decimal CuryInclTaxTotal, decimal CuryWhTaxTotal,
Action<object, decimal, decimal, decimal> del)
{
del?.Invoke(row, CuryTaxTotal, CuryInclTaxTotal, CuryWhTaxTotal);
var someOtherTotal = Base1.Documents.Cache.GetValueAsDecimal<CROpportunityExtension.aMCurySomeOtherTotal>(row);
if (someOtherTotal == 0)
{
return;
}
var curyDocTotal = someOtherTotal + Base1.Documents.Cache.GetValueAsDecimal<CROpportunity.curyProductsAmount>(row);
Base1.Documents.Cache.SetValue<CROpportunity.curyProductsAmount>(row, curyDocTotal);
}
}
What is going on inside of CalcDocTotals in my graph extension is not the focus. It is the fact that I cannot override the OpportunityMaint.SalesTax CalcDocTotals method as I could if the method was in the first level (Base) graph. The SalesTax graph extension has the method as protected but protected methods (if it was in the base graph) are overrideable using the PXOverrideAttribute if you make your method call public which is what I have done. I also tried using a declared delegate in place of the Action but same results (as I expected but wanted to confirm).
My question: Is it possible to override a second, third, etc. level graph extension method using the PXOverrideAttribute?
When I compile the code above and the page loads I get this error:
Method Void CalcDocTotals(System.Object, System.Decimal,
System.Decimal, System.Decimal,
System.Action`4[System.Object,System.Decimal,System.Decimal,System.Decimal])
in graph extension is marked as [PXOverride], but the original method
with such name has not been found in PXGraph
The ability to override extension methods from a higher level extension has been added in 2018R1 Update 4 (18.104.0023). This resolves my question/issue and allows for the code posted in my question to function as is.
You cannot override methods from Extension1 in Extension2 etc as far as I've been able to see in my years with Acumatica. My solution to the problem was thus : Create a helper graph with your basic methodology, create a field or property for it in whatever graph you wish to use it in (Preferably a Lazy initialized one), then in whatever project you must override the logic on, just reference your original project, and create an extension of your helper graph.

Dapper does not warn or fail with missing data

Let's say I have a class (simplistic for example) and I want to ensure that the PersonId and Name fields are ALWAYS populated.
public class Person
{
int PersonId { get; set; }
string Name { get; set; }
string Address { get; set; }
}
Currently, my query would be:
Person p = conn.Query<Person>("SELECT * FROM People");
However, I may have changed my database schema from PersonId to PID and now the code is going to go through just fine.
What I'd like to do is one of the following:
Decorate the property PersonId with an attribute such as Required (that dapper can validate)
Tell dapper to figure out that the mappings are not getting filled out completely (i.e. throw an exception when not all the properties in the class are filled out by data from the query).
Is this possible currently? If not, can someone point me to how I could do this without affecting performance too badly?
IMHO, the second option would be the best because it won't break existing code for users and it doesn't require more attribute decoration on classes we may not have access to.
At the moment, no this is not possible. And indeed, there are a lot of cases where it is actively useful to populate a partial model, so I wouldn't want to add anything implicit. In many cases, the domain model is an extended view on the data model, so I don't think option 2 can work - and I know it would break in a gazillion places in my code ;p If we restrict ourselves to the more explicit options...
So far, we have deliberately avoided things like attributes; the idea has been to keep it as lean and direct as possible. I'm not pathologically opposed to attributes - just: it can be problematic having to probe them. But maybe it is time... we could perhaps also allow simple column mapping at the same time, i.e.
[Map(Name = "Person Id", Required = true)]
int PersonId { get; set; }
where both Name and Required are optional. Thoughts? This is problematic in a few ways, though - in particular at the moment we only probe for columns we can see, in particular in the extensibility API.
The other possibility is an interface that we check for, allowing you to manually verify the data after loading; for example:
public class Person : IMapCallback {
void IMapCallback.BeforePopulate() {}
void IMapCallback.AfterPopulate() {
if(PersonId == 0)
throw new InvalidOperationException("PersonId not populated");
}
}
The interface option makes me happier in many ways:
it avoids a lot of extra reflection probing (just one check to do)
it is more flexible - you can choose what is important to you
it doesn't impact the extensibility API
but: it is more manual.
I'm open to input, but I want to make sure we get it right rather than rush in all guns blazing.

Return Only Certain Fields From Lucene Search

I'm using Lucene to search an index and it works fine. My only issue is that I only need one particular field of what is being returned. Can you specify to Lucene to only return a certain field in the results and not the entire document?
This is why FieldSelector class exists.
You can implement a class like this
class MyFieldSelector : FieldSelector
{
public FieldSelectorResult Accept(string fieldName)
{
if (fieldName == "field1") return FieldSelectorResult.LOAD_AND_BREAK;
return FieldSelectorResult.NO_LOAD;
}
}
and use it as indexReader.Document(docid,new MyFieldSelector());
If you are interested in loading a small field, this will prevent to load large fields which, in turn, means a speed-up in loading documents. I think you can find much more detailed info by some googling.
What do you mean "return certain fields"? The Document.get() function returns just the field you request.
Yes, you can definitely do what you are asking. All you have to do is include the field name (case-sensitive) in the document.get() method.
string fieldNameText = doc.Get("fieldName");
FYI, it's usually a good idea to include some code in your questions. It makes it easier to provide a good answer.

How to Inner Join an UDF Function with parameters using SubSonic

I need to query a table using FreeTextTable (because I need ranking), with SubSonic. AFAIK, Subsonic doesn't support FullText, so I ended up creating a simple UDF function (Table Function) which takes 2 params (keywords to search and max number of results).
Now, how can I inner join the main table with this FreeTextTable?
InlineQuery is not an option.
Example:
table ARTICLE with fields Id, ArticleName, Author, ArticleStatus.
The search can be done by one of more of the following fields: ArticleName (fulltext), Author (another FullText but with different search keywords), ArticleStatus (an int).
Actually the query is far more complex and has other joins (depending on user choice).
If SubSonic cannot handle this situation, probably the best solution is good old plain sql (so there would be no need to create an UDF, too).
Thanks for your help
ps: will SubSonic 3.0 handle this situation?
3.0 can do this for you but you'd need to make a template for it since we don't handle functions (yet) out of the box. I'll be working on this in the coming weeks - for now I don't think 2.2 will do this for you.
I realize your question is more complex than this, but you can get results from a table valued function via SubSonic 2.2 with a little massaging.
Copy the .cs file from one of your generated views into a safe folder, and then change all the properties to match the columns returned by your UDF.
Then, on your collection, add a constructor method with your parameters and have it execute an InlineQuery.
public partial class UDFSearchCollection
{
public UDFSearchCollection(){}
public UDFSearchCollection(string keyword, int maxResults)
{
UDFSearchCollection coll = new InlineQuery().ExecuteAsCollection<UDFSearchCollection>("select resultID, resultColumn from dbo.udfSearch(#keyword, #maxResults)",keyword,maxResults);
coll.CopyTo(this);
coll = null;
}
}
public partial class UDFSearch : ReadOnlyRecord<UDFSearch>, IReadOnlyRecord
{
//all the methods for read only record go here
...
}
An inner join would be a little more difficult because the table object doesn't have it's own parameters collection. But it could...

Resources