AMDP to convert quantities based on MARM
As a developer with over 20 years of experience, this is my very first blog on blogs.sap.com. But don’t let that fool you: i have been a visitor of the old SCN forum, posted numerous times (not with this S-id but my old ones) and oh how i miss the old coffee corner…
During those years i have seen all sorts of new solutions/technologies such as DCOM Component Connector, ITS, Webdynpro and so on being introduced by SAP. I always tried to keep up with these new developments.
And now we live in the era of Hana, the era of push-the-code-down. Or if you like: the era of code-to -data.
On my current position i participated in 2015/2016 in probably one of the first Hana DB migrations in The Netherlands. And my initial thought was: nice i got me some new goodies to play with. But reality was different. Core Data Services and ABAP Managed Database Procedures: it scared the hell out of me. I wasn’t feeling very comfortable to adopt these new toys offered by SAP. So it took me almost a year to convince myself to start exploring the possibilites i was given.
Now it’s 2018 and in the past year i have “mastered” the art of CDS and gradually becoming more and more proficient in using AMDP. Using CDS and AMDP i solved many performance issues on Business Suite on Hana and CAR on Hana. But something kept nagging me: the idea that i wasn’t fully applying the code-to-data concept. And to be honest: I wasn’t. Surely alot of logic was pushed down to the database, but still the application layer still had enough to do (processing that otherwise could have been done on the database as well). And with my very first success on code-to-data on converting a big chunk custom development into an AMDP came the hunger for more.
Now i’m tasked with developing a transaction that has to do a lot of unit of measurement conversions. In the pre-Hana world i would simply use function MD_CONVERT_MATERIAL_UNIT. But that would be too easy and would be the opposite of what i want to achieve: code-to-data.
So i called my bff Google and found two blogs that looked very promising:
Creating Quantity Unit of Measure Conversion in HANA by Tim Korba and
How to…implement HANA dynamic material UOM conversions by Justin Molenaur
But these solutions rely on the use of Hana modelling and i wanted something that was easy to read and implement. So my search continued and I found two AMDP’s that looked very promising:
- CL_CS_BOM_AMDP=>MAT_CONVERT_TO_BASE_UOM
and
- CL_CS_BOM_AMDP=>MAT_CONVERT_UOM_TO_BASE_UOM
Mind you: i’m working on a Business suite on Hana with Netweaver 7.4 SP12 and the availabilty of AMDP’s is very scarce.
Unfortunately, both didn’t meet my requirements: they both convert a quantity to the base unit of measurement of a material. And i want a solution that returns the same result like function MD_CONVERT_MATERIAL_UNIT. It struck me as odd that SAP didn’t already have a solution for a simple unit conversion. So much for being lazy and now i had put my mind at work to come up with a solution.
After trying out several approaches (rewriting both found AMDP’s, writing code from scratch) and wasting several hours of trying to figure out a proper formula, it finally hit me.
The way SAP presents the conversion rules in for instance MM41 and MM42 is not how the conversion rules are actually stored in table MARM. Each conversion rule in table MARM is always in relation to the base unit of measurement. Even if there is a Lower-Level Unit of Measure in a Packing Hierarchy (MARM-MESUB).
When presenting these kind of conversion rules in the GUI, you will always see the conversion rule to MARM-MESUB.
But the table MARM will show something differently:
Notice the difference between the presentation of CS in the GUI and how it is stored in MARM: always in relation to the base unit of measurement.
With that realization it was no longer a problem to come up with a formula:
converted quantity = original quantity * ((denominator uom from / nominator uom from) / (denominator uom to / nominator uom to))
Now i could write the AMDP i so badly wanted to have and that would gave me the same result as MD_CONVERT_MATERIAL_UNIT and i gladly want to share it for anyone to profit from if.
CLASS zcl_amdp_unit_of_measurement DEFINITION
PUBLIC
FINAL
CREATE PUBLIC .
PUBLIC SECTION.
INTERFACES if_amdp_marker_hdb.
TYPES: BEGIN OF ty_data,
matnr TYPE matnr,
menge_from TYPE menge_d,
uom_from TYPE meins,
menge_to TYPE menge_d,
uom_to TYPE meins,
END OF ty_data,
tty_data TYPE TABLE OF ty_data.
CLASS-METHODS marm_convert_material_unit IMPORTING VALUE(it_data) TYPE tty_data
EXPORTING VALUE(et_result) TYPE tty_data.
PROTECTED SECTION.
PRIVATE SECTION.
ENDCLASS.
CLASS zcl_amdp_unit_of_measurement IMPLEMENTATION.
METHOD marm_convert_material_unit BY DATABASE PROCEDURE FOR HDB
LANGUAGE SQLSCRIPT
OPTIONS READ-ONLY
USING mara
marm.
* The standard SAP AMDP CL_CS_BOM_AMDP=>MAT_CONVERT_UOM_TO_BASE_UOM does not factor in MARM-MESUB.
* Therefore, calculation will be incomplete if MESUB is used.
*
* EXAMPLE: material with base UoM PCE and the following UoM conversion rules
* 1 PCE = 1 PCE
* 1 CS = 4 MP (MESUB = MP)
* 1 MP = 10 PCE
*
* When using CL_CS_BOM_AMDP=>MAT_CONVERT_UOM_TO_BASE_UOM the result is:
* 1 MP => 10 PCE
* 1 CS => 4 PCE
*
* This is not correct and should be:
* 1 CS = 4 MP and 1 MP = 10 PCE thus 1 CS = 4*10 PCE = 40 PCE
* Since all UoM conversions rules are stored in MARM in relation to the base UoM (even with MESUB), with a simple formula we
* can make sure the correct calculation is performed and get a converted quantity just like using function MD_CONVERT_MATERIAL_UNIT:
* Formula to convert quantity unit_from = > unit_to
* converted quantity = quantity * ( ( denominator uom_from / nominator uom_from ) / ( denominator uom_to / nominator uom_to ) )
tmp_from =
select marm.mandt as mandt,
marm.matnr as matnr,
marm.meinh as uom,
marm.umrez as umrez,
marm.umren as umren
from :it_data as data_source
inner join mara as mara on mara.mandt = session_context( 'CLIENT' ) and
mara.matnr = data_source.matnr
inner join marm as marm on marm.mandt = mara.mandt and
marm.matnr = data_source.matnr and
marm.meinh = data_source.uom_from;
tmp_to =
select marm.mandt as mandt,
marm.matnr as matnr,
marm.meinh as uom,
marm.umrez as umrez,
marm.umren as umren
from :it_data as data_source
inner join mara as mara on mara.mandt = session_context( 'CLIENT' ) and
mara.matnr = data_source.matnr
inner join marm as marm on marm.mandt = mara.mandt and
marm.matnr = data_source.matnr and
marm.meinh = data_source.uom_to;
* Convert original quantity to requested unit
* converted quantity = quantity * ( ( denominator uom_from / nominator uom_from ) / ( denominator uom_to / nominator uom_to ) )
et_result =
select data_source.matnr,
data_source.menge_from,
data_from.uom as uom_from,
ROUND( ( data_source.menge_from * ( ( data_from.umrez / data_from.umren ) / ( data_to.umrez / data_to.umren) ) ), 3, ROUND_HALF_EVEN) as menge_to,
data_to.uom as uom_to
from :it_data as data_source
left join :tmp_from as data_from on data_from.matnr = data_source.matnr and
data_from.uom = data_source.uom_from
left join :tmp_to as data_to on data_to.matnr = data_source.matnr and
data_to.uom = data_source.uom_to;
ENDMETHOD.
ENDCLASS.
To conclude: I had fun in writing this blog and I hope you can put the AMDP i wrote to good use. I have no idea if SAP will deliver their standard AMDP that will do the same. But until then, consider this as a nice alternative that you can implement and use.
I have done several comparisons with function MD_CONVERT_MATERIAL_UNIT and have yet to find any differences/incorrect results. Maybe someone can break the code in which case i’d be happy to know about it so I can adjust my code.
Version 2.
After posting this blog, i had another look on the AMDP and decided the amount of code could be less. So here’s a newer version.
METHOD marm_convert_material_unit BY DATABASE PROCEDURE FOR HDB
LANGUAGE SQLSCRIPT
OPTIONS READ-ONLY
USING marm.
* The standard SAP AMDP CL_CS_BOM_AMDP=>MAT_CONVERT_UOM_TO_BASE_UOM does not factor in MARM-MESUB.
* Therefore, calculation will be incomplete if MESUB is used.
*
* EXAMPLE: material with base UoM PCE and the following UoM conversion rules
* 1 PCE = 1 PCE
* 1 CS = 4 MP (MESUB = MP)
* 1 MP = 10 PCE
*
* When using CL_CS_BOM_AMDP=>MAT_CONVERT_UOM_TO_BASE_UOM the result is:
* 1 MP => 10 PCE
* 1 CS => 4 PCE
*
* This is not correct and should be:
* 1 CS = 4 MP and 1 MP = 10 PCE thus 1 CS = 4*10 PCE = 40 PCE
* Since all UoM conversions rules are stored in MARM in relation to the base UoM (even with MESUB), with a simple formula we
* can make sure the correct calculation is performed and get a converted quantity just like using function MD_CONVERT_MATERIAL_UNIT:
* Formula to convert quantity unit_from = > unit_to
* converted quantity = quantity * ( ( denominator uom_from / nominator uom_from ) / ( denominator uom_to / nominator uom_to ) )
et_result =
select data_source.matnr,
data_source.menge_from,
data_source.uom_from,
case when marm_to.umren = 0 or marm_to.umren is NULL then 0.000
when marm_from.umren = 0 or marm_from.umren is NULL then 0.000
when marm_to.umren is NOT NULL and marm_from.umren is NOT NULL then
ROUND( ( data_source.menge_from * ( ( marm_from.umrez / marm_from.umren ) / ( marm_to.umrez / marm_to.umren) ) ), 3, ROUND_HALF_EVEN)
end as menge_to,
marm_to.meinh as uom_to
from :it_data as data_source
left outer join marm as marm_from on marm_from.mandt = session_context( 'CLIENT' ) and
marm_from.matnr = data_source.matnr and
marm_from.meinh = data_source.uom_from
left outer join marm as marm_to on marm_to.mandt = session_context( 'CLIENT' ) and
marm_to.matnr = data_source.matnr and
marm_to.meinh = data_source.uom_to;
ENDMETHOD.
Version 3.
This version uses the conversion factor for UoM in T006 and T006D. If a normal conversion fails because a required UoM is not maintained in the masterdata, another conversion is executed against T006 and T006D. But the UoM from and UoM to must be both of the same dimension and at least 1 UoM is maintained in the masterdata.
METHOD marm_convert_material_unit BY DATABASE PROCEDURE FOR HDB
LANGUAGE SQLSCRIPT
OPTIONS READ-ONLY
USING marm.
* The standard SAP AMDP CL_CS_BOM_AMDP=>MAT_CONVERT_UOM_TO_BASE_UOM does not factor in MARM-MESUB.
* Therefore, calculation will be incomplete if MESUB is used.
*
* EXAMPLE: material with base UoM PCE and the following UoM conversion rules
* 1 PCE = 1 PCE
* 1 CS = 4 MP (MESUB = MP)
* 1 MP = 10 PCE
*
* When using CL_CS_BOM_AMDP=>MAT_CONVERT_UOM_TO_BASE_UOM the result is:
* 1 MP => 10 PCE
* 1 CS => 4 PCE
*
* This is not correct and should be:
* 1 CS = 4 MP and 1 MP = 10 PCE thus 1 CS = 4*10 PCE = 40 PCE
* Since all UoM conversions rules are stored in MARM in relation to the base UoM (even with MESUB), with a simple formula we
* can make sure the correct calculation is performed and get a converted quantity just like using function MD_CONVERT_MATERIAL_UNIT:
* Formula to convert quantity unit_from = > unit_to
* converted quantity = quantity * ( ( denominator uom_from / nominator uom_from ) / ( denominator uom_to / nominator uom_to ) )
tmp_result =
select data_source.matnr,
data_source.menge_from,
data_source.uom_from,
case when marm_to.umren = 0 or marm_to.umren is NULL then 0.000
when marm_from.umren = 0 or marm_from.umren is NULL then 0.000
when marm_to.umren is NOT NULL and marm_from.umren is NOT NULL then
ROUND( ( data_source.menge_from * ( ( marm_from.umrez / marm_from.umren ) / ( marm_to.umrez / marm_to.umren) ) ), 3, ROUND_HALF_EVEN)
end as menge_to,
marm_to.meinh as uom_to
from :it_data as data_source
left outer join marm as marm_from on marm_from.mandt = session_context( 'CLIENT' ) and
marm_from.matnr = data_source.matnr and
marm_from.meinh = data_source.uom_from
left outer join marm as marm_to on marm_to.mandt = session_context( 'CLIENT' ) and
marm_to.matnr = data_source.matnr and
marm_to.meinh = data_source.uom_to;
* If no conversion could be carried out, retry with standard CONVERT_UNIT
et_result =
select matnr,
menge_from,
uom_from,
menge_to,
uom_to
from :tmp_result
where menge_to <> 0 and menge_to is NOT NULL
union all
select no_cnv.matnr,
no_cnv.menge_from,
no_cnv.uom_from,
CONVERT_UNIT("QUANTITY"=>no_cnv.menge_from,
"SOURCE_UNIT_COLUMN"=>no_cnv.uom_from,
"SCHEMA"=>'SAPSR3',
"TARGET_UNIT_COLUMN"=>data_source.uom_to,
"ERROR_HANDLING"=>'set to null',
"CLIENT"=>session_context('CLIENT')) AS menge_to,
data_source.uom_to
from :tmp_result as no_cnv
inner join :it_data as data_source on no_cnv.matnr = data_source.matnr
where no_cnv.menge_to = 0 or no_cnv.menge_to is NULL;
ENDMETHOD.
Example: material A has a conversion factor of 1 PCE = 1 KG. Now you can calculate the amount in unit TO. Even though TO is not maintained.
Regards,
Guus
Very cool! I can't wait to get my hands on these new tools!
I must add coffee corner is still alive and waiting for some interesting posts / answers. It's a strange URL. It is at the top interesting menu at the top under community, sap community. Feel free to add them. We need some fun things to "talk" about sometimes too.
Michelle
Thanks! I had a look on the coffee corner but somehow the 'old familiar' feeling is missing. Maybe it's the look and feel, not sure.
Hi Guus,
thanks a lot for your blog!
I haven't gotten deep into AMDP yet, but I do look at CDS, with I would say goes in the same family 😉
As you already suggested, one would expect that SAP delivers such standard features, and you shouldn't need to craft it yourself - but great that you did! 🙂
As you said "there should be a database feature (AMDP), doing on the DB what MD_CONVERT_MATERIAL_UNIT does on ABAP-Level", I have the same feeling for calculation of AtP quantity, so I'd like to have a database version of BAPI_MATERIAL_AVAILABILITY .
I would wish I could just select AtP_quantity from a CDS-view – but so far, I seem to be the only one able to imagine that this could even be possible!
Colleagues tell me: "ATP is a very complicated calculation, I don't think this can be done!" but I think: While I agree that it’s complicated - if it can be calculated in ABAP (BAPI_MATERIAL_AVAILABILITY), someone (not me, but some smart person @SAP) should be able to calculate it on DB-Level (CDS/AMDP).
Maybe you have something to put in on my question regarding this?
-> https://answers.sap.com/questions/357852/s4hana-get-atp-quantity-from-cds.html
best
Joachim
Hello Guus,
Very interesting blog. I was also wondering why SAP hasn’t made something built in for material quantities conversions…
I see one difference with MD_CONVERT_MATERIAL_UNIT : the function module handle basic conversion in order to match the unit that is defined in MARM.
For example, with your AMDP, if you have defined unit conversion in MARM in Kg, you can’t get the quantity in tonne.
I don’t know if we can improve your AMDP to handle this…
Hello Louis-Arnaud,
you are absolutely right. My focus was on only those UoM's maintained in the material masterdata. But i have posted a version 3 with which i think i cover the part you mentioned.
Thanks for that!
Guus
Thanks a lot Guus ! I will certainly reuse this code in my future projects !
Hello Guus,
I tried to use the table function in a CDS view in order to add column with quantity in base unit to quantity in Kg.
I don't understand how to do this, because it is not possible to pass column to table function parameters... I would like to use the table function the same way currency conversion is available in CDS. Do you achieve to do something like that ?
Thank you
When i have the time, i will create a CDS table function (which in fact is an AMDP) that can be used in any CDS view.
Hi Guus,
When I try to call CONVERT_UNIT from my AMDP I get an invalid name of function or procedure error. Any ideas of what I'm missing?
Regards.
Not sure why it fails for you. Happen to know what the exact error message is?
Do you have a sample coding how you used this class in bw amdp tranformation routine?
Thanks.