I have recently modified the default report 207 Sales Invoice and created a custom report. One request I had, was to display Shipment information on this invoice for each shipment that the current invoice covers. Particularly I needed to show the following values:
Shipment No. from the Delivery Note
Quantity of the Shipment
The standard report only shows the posted shipment date and the quantity and that only for one shipment (I believe its the last one). It does not show the "Shipment No.".
When making the required changes I had several problems and it was difficult to find any information about the findPostedShipmentDate() function, so I have decided to post my solution here.
The question would be:
How can I show multiple shipments on the sales invoice?
How can I show the "Shipmente No." and the individual quantities or each shipment?
In the function "Sales Invoice Line"::OnAfterGetRecord() the function findPostedShipmentDate() is called. This function performs several checks to find the posting date and if necessary calls the function GenerateBufferFromValueEntry() which fills the table "SalesShipmentBuffer" which temporary stores all the shipment information for the current "Sales Invoice Line" record:
CASE "Sales Invoice Line".Type OF
"Sales Invoice Line".Type::Item:
GenerateBufferFromValueEntry("Sales Invoice Line");
...
Afterwards it performs several checks on the buffer table and deletes the entries again (more on this later).
In the GenerateBufferFromValueEntry the following happens. The actual shipment information is stored in the ItemLedgerEntry (32) table, in order to find the correct rows, the ValueEntry (5802) table is used, which contains the right key (ValueEntry."Item Ledger Entry No.").
Additionally to the existing range filters I needed to add one line to limit the range to the Item No. and I had to remove the range filter for the Entry No.. I also modified the function AddBufferEntry to additionally take the ItemLedgerEntry."Document No." which contains the Shipment No. from the delivery note. Here is the complete code:
TotalQuantity := SalesInvoiceLine2."Quantity (Base)";
ValueEntry.SETCURRENTKEY("Document No.");
ValueEntry.SETRANGE("Document No.",SalesInvoiceLine2."Document No.");
ValueEntry.SETRANGE("Posting Date","Sales Invoice Header"."Posting Date");
ValueEntry.SETRANGE(ValueEntry."Item No.", "Sales Invoice Line"."No."); //Added
ValueEntry.SETRANGE("Item Charge No.",'');
//ValueEntry.SETFILTER("Entry No.",'%1..',FirstValueEntryNo); //Removed
IF ValueEntry.FIND('-') THEN BEGIN
REPEAT
IF ItemLedgerEntry.GET(ValueEntry."Item Ledger Entry No.") THEN BEGIN
IF SalesInvoiceLine2."Qty. per Unit of Measure" <> 0 THEN
Quantity := ValueEntry."Invoiced Quantity" /
SalesInvoiceLine2."Qty. per Unit of Measure"
ELSE
Quantity := ValueEntry."Invoiced Quantity";
AddBufferEntry(
SalesInvoiceLine2,
-Quantity,
ItemLedgerEntry."Posting Date",
ItemLedgerEntry."Document No."); //Added
TotalQuantity := TotalQuantity + ValueEntry."Invoiced Quantity";
END;
FirstValueEntryNo := ValueEntry."Entry No." + 1;
UNTIL (ValueEntry.NEXT = 0) OR (TotalQuantity = 0);
END;
In the FindPostedShipmentDate function I also deleted the first couple of lines to prevent the function from exiting before the SalesShipmentBuffer had been filled:
IF "Sales Invoice Line"."Shipment No." <> '' THEN
IF SalesShipmentHeader.GET("Sales Invoice Line"."Shipment No.") THEN
EXIT(SalesShipmentHeader."Posting Date");
IF "Sales Invoice Header"."Order No."='' THEN
EXIT("Sales Invoice Header"."Posting Date");
In the last part of FindPostedShipmentDate the records of SalesShipmentBuffer are deleted again, which might seem a bit strange at first (it did to me). The reason is that even after the DELETE and DELETEALL functions are called, the record variable retains the values that were stored in the record before calling these functions. So the result is that the SalesShipmentBuffer tables is cleared, such that its empty for the next run of the findPostedShipmentDate() function, but the values can still be used in the report. The other confusing part is that actually does a very simple thing which is made to look very complicated. If the buffer contains only one line it deletes the line, otherwise if it contains more than one line it first calculates the sum of all quantity fields and then deletes all lines.
Anyway, I only had to make one change, which was removing the following line since the Document No. field now contains the Shipment No. and not the Invoice No.
SalesShipmentBuffer.SETRANGE("Document No.","Sales Invoice Line"."Document No.");
Here is the remaining code:
SalesShipmentBuffer.SETRANGE("Line No." ,"Sales Invoice Line"."Line No.");
SalesShipmentBuffer.SETRANGE("No." ,"Sales Invoice Line"."No.");
IF SalesShipmentBuffer.FIND('-') THEN BEGIN
SalesShipmentBuffer2 := SalesShipmentBuffer;
IF SalesShipmentBuffer.NEXT = 0 THEN BEGIN
SalesShipmentBuffer.GET(
SalesShipmentBuffer2."Document No.",
SalesShipmentBuffer2."Line No.",
SalesShipmentBuffer2."Entry No."
);
SalesShipmentBuffer.DELETE;
EXIT(SalesShipmentBuffer2."Posting Date");
END ;
SalesShipmentBuffer.CALCSUMS(Quantity);
IF SalesShipmentBuffer.Quantity <> "Sales Invoice Line".Quantity THEN BEGIN
SalesShipmentBuffer.DELETEALL;
EXIT("Sales Invoice Header"."Posting Date");
END;
END ELSE
EXIT("Sales Invoice Header"."Posting Date");
That's it. Now I just had to modify the DataItem source entries to show the necessary fields on the report.
The changes I had to make are actually not so many, but the code is not documented at all and I could not find much information when I googled for this problem or the functions involved. So I hope my post is helpful, in case someone has a similar problem than I had.
On a side not, I do not understand why the above code segment is written in such a wired why, I belive (though I have not verified this), that it could be simplified into the following code, which is much more readable. Please leave comments if you feel that my analysis is wrong.
SalesShipmentBuffer.SETRANGE("Line No." ,"Sales Invoice Line"."Line No.");
SalesShipmentBuffer.SETRANGE("No." ,"Sales Invoice Line"."No.");
SalesShipmentBuffer.FINDFIRST;
IF SalesShipmentBuffer.COUNT > 0 THEN BEGIN
IF SalesShipmentBuffer.COUNT = 1 THEN BEGIN
SalesShipmentBuffer.DELETE;
EXIT(SalesShipmentBuffer2."Posting Date");
END ELSE BEGIN
SalesShipmentBuffer.CALCSUMS(Quantity);
SalesShipmentBuffer.DELETEALL;
EXIT("Sales Invoice Header"."Posting Date");
END;
END ELSE
EXIT("Sales Invoice Header"."Posting Date");
Related
I have a table ORDERS which contains column ORDER_DATE. I have created a Chart as a Bar type. I want the chart to show the amount of orders in a given date or range.
I'm following this Youtube tutorial which shows how to create a datepicker that returns a range in a Report. I'm trying to replicate this in a chart.
What I've done so far
Created datepicker items P5_DATE_FROM and P5_DATE_TO
Changed the Series Source Type to SQL Query
select ORDER_ID,
ORDER_NUMBER,
TO_CHAR(ORDER_DATE, 'YYYY-MM-DD') AS ORDERDATE,
STORE_ID,
FULL_NAME,
EMAIL,
CITY,
STATE,
ZIP_CODE,
CREDIT_CARD,
ORDER_ITEMS,
APEX$SYNC_STEP_STATIC_ID,
APEX$ROW_SYNC_TIMESTAMP
from ORDERS_LOCAL
where TO_CHAR(ORDER_DATE, 'YYYY-MM-DD') between :P5_DATE_FROM and :P5_DATE_TO
Source Page Items to Submit added P5_DATE_FROM,P5_DATE_TO
I basically followed the exact steps of the video. However, since the video is for a report and mine is for a chart, the chart isn't actually returning any data.
I think this is because for charts there are additional fields I need to configure. I noticed the video didn't cover Column Mapping and I'm a bit confused to what to enter here.
----------------UPDATE-------------
I've followed Koen's instructions from his answer. It seems like to TO_CHAR function was causing the error. I've updated the SQL Query to below:
select ORDER_ID,
ORDER_NUMBER,
ORDER_DATE,
STORE_ID,
FULL_NAME,
EMAIL,
CITY,
STATE,
ZIP_CODE,
CREDIT_CARD,
ORDER_ITEMS,
APEX$SYNC_STEP_STATIC_ID,
APEX$ROW_SYNC_TIMESTAMP
from ORDERS_LOCAL
where ORDER_DATE between TO_DATE(:P5_DATE_FROM,'YYYY-MM-DD') and TO_DATE(:P5_DATE_TO,'YYYY-MM-DD')
However, on the Page Designer I cannot save and run page until I select Column Mapping - Label and Value. I've set the Label as ORDER_DATE but I am unsure of what to select for the Value.
Setting the Value to ORDER_DATE shows an error Ajax call returned server error ORA-01403: no data found for CHART Count Orders by Date.
and selecting any other Value such as ORDER_NUMBER or ZIP_CODE populates the chart with the actual integer value of the column (ex: ZIP_CODE returns a chart of x-axis: date, y-axis: actual zip code numbers)
I see 2 possible issues.
You submit P5_DATE_FROM and P5_DATE_TO but the source has P1_DATE_FROM and P1_DATE_TO - not sure that is a typo but it should all be the same...
The where clause is wrong. This
from ORDERS_LOCAL
where TO_CHAR(ORDER_DATE, 'MM-DD-YYYY') between :P1_DATE_FROM and :P1_DATE_TO
should be
from ORDERS_LOCAL
where ORDER_DATE between TO_DATE(:P1_DATE_FROM,'MM-DD-YYYY') and TO_DATE(:P1_DATE_TO,'MM-DD-YYYY')
Reason: the column orders_local.order_date is of datatype DATE. If you convert it to a string using TO_CHAR then it will be a string comparison, not a date comparison.
Note that bind variables P1_DATE_FROM and P1_DATE_TO are strings - they're defined in the DOM on the html page and that has no concept of oracle datatypes so everything is just a string. If they're used as such in a date comparison, you're relying on implicit conversion by the database. It's safer to do explicit conversion using the TO_DATE function.
--UPDATE--
Your question starts with "I want the chart to show the amount of orders in a given date or range.". Well... there is your answer. The "Value" is the amount of orders on each day. Update your source query to include a count of the orders and group by day. Then make your value attribute the column that has the count.
This question is a follow up to another SO question
I want a bar chart to show the amount of orders in a given date or range. Koen's updated answer shows:
'The "Value" is the amount of orders on each day. Update your source query to include a count of the orders and group by day. Then make your value attribute the column that has the count.'
How would I go about doing this?
Summary: I have a table ORDERS which contains column ORDER_DATE. I have created a Chart as a Bar type. I want the chart to show the amount of orders in a given date or range.
I'm following this Youtube tutorial which shows how to create a datepicker that returns a range in a Report. I'm trying to replicate this in a chart.
What I've done so far
Created datepicker items P5_DATE_FROM and P5_DATE_TO
Changed the Series Source Type to SQL Query
select ORDER_ID,
ORDER_NUMBER,
ORDER_DATE,
STORE_ID,
FULL_NAME,
EMAIL,
CITY,
STATE,
ZIP_CODE,
CREDIT_CARD,
ORDER_ITEMS,
APEX$SYNC_STEP_STATIC_ID,
APEX$ROW_SYNC_TIMESTAMP
from ORDERS_LOCAL
where ORDER_DATE between TO_DATE(:P5_DATE_FROM,'YYYY-MM-DD') and TO_DATE(:P5_DATE_TO,'YYYY-MM-DD')
Source Page Items to Submit added P5_DATE_FROM,P5_DATE_TO
I basically followed the exact steps of the video.
However, on the Page Designer I cannot save and run page until I select Column Mapping - Label and Value. I've set the Label as ORDER_DATE but I am unsure of what to select for the Value.
Setting the Value to ORDER_DATE shows an error Ajax call returned server error ORA-01403: no data found for CHART Count Orders by Date.
and selecting any other Value such as ORDER_NUMBER or ZIP_CODE populates the chart with the actual integer value of the column (ex: ZIP_CODE returns a chart of x-axis: date, y-axis: actual zip code numbers)
-----------UPDATE----------
Per Koen's answer, I've updated the Source SQL Query to below but I am now receiving an error.
And if I copy the exact query that Koen suggested, I run into below:
I did some messing around and found if I include APEX$SYNC_STEP_STATIC_ID and APEX$ROW_SYNC_TIMESTAMP the missing expression error goes away but instead I receive the GROUP BY error.
Something like this (untested) should work. Use TRUNC to ensure all orders on the same date are grouped (since date has a time portion, you'd have a different column for each different date time. Use ORDER_DATE as label and ORDER_COUNT as value.
select COUNT(ORDER_ID) AS ORDER_COUNT,
TRUNC(ORDER_DATE) AS ORDER_DATE,
from ORDERS_LOCAL
where ORDER_DATE between TO_DATE(:P5_DATE_FROM,'YYYY-MM-DD') and TO_DATE(:P5_DATE_TO,'YYYY-MM-DD')
GROUP BY TRUNC(ORDER_DATE)
I'd like to group a table by a field- employer code and then calculate the sum of a decimal field- total contribution in that group from C/AL code. Here's my table structure
Employer No_ Total Contribution
PRTEMP005022 1817.64
PRTEMP005022 1782
PRTEMP005022 2049.3
PRTEMP005022 1568.16
PR0000247148 47750.62
PR0000247148 47532.81
PU0000400011 5314.52
PU0000400011 5314.52
PU0000400011 17225.83
PU0000400011 4509.61
STRV00000000 6088.72
STRV00000000 4065.36
STRV00000000 2191.18
STRV00000000 3485.42
STRV00000000 4709.77
How can I assign the employer code and the sum of the total contributions for that employer to variables from C/AL code
I'll assume that you have the employers in a seperate table called Employers. Otherwise you will have to create the values to group by in a temporary table (table Name/Value Buffer is userful there).
Employer.RESET();
//Employer.SETRANGE(... filtering if needed
IF Employer.FINDSET() THEN BEGIN
REPEAT
YourTable.RESET();
YourTable.SETRANGE("Employer No.", Employer."No.");
YourTable.CALCSUMS("Total Contribution");
TotalContribution := YourTable."Total Contribution";
EmployerNo := YourTable."Employer No.";
UNTIL Employer.NEXT() = 0;
END;
Please be aware that the variables TotalContribution and EmployerNo will be overwritten on every iteration of the loop so you will have to do something with them.
I have got Gen Bus. Posting Group field showing on the subform of Sales Order. This Subform is also showing Location Code for the Line.
I am attempting to write a logic as such that if the User selects Location Code as DROPSHIP, the Gen Bus. Posting Group should be LOCAL-DROPSHIP.
LOCAL-DROPSHIP is a record in the Gen. Bus. Posting Group. I have created it before writing this code. However, it would not assign to my rec.Gen Bus. Posting Group.
Here is the code on the Subform;
modify("Location Code")
{
trigger OnAfterValidate()
var
recLocation: Record Location;
recCustomer: Record Customer;
recSalesLine: Record "Sales Line";
recGenPosting: Record "Gen. Business Posting Group";
begin
recGenPosting.Get('LOCAL-DS');
if rec."Location Code" = 'DROPSHIP' then begin
Message(recGenPosting.Code);
// Validate("Gen. Bus. Posting Group", recGenPosting.Code);
Rec."Gen. Bus. Posting Group" := recGenPosting.Code;
CurrPage.Update();
end;
end;
}
You should do this as Table Extension of Table Sales Line instead of Page Extension. Validate is important in this case.
Extended Sales Line table, on the OnModify trigger, wrote;
if rec."Location Code" = 'DROPSHIP' then begin
If recCustomer.Get(REC."Sell-to Customer No.") THEN begin
if recCustomer."Country/Region Code" = 'CITY' then begin
rec."Gen. Bus. Posting Group" := 'DS';
rec.Modify();
end
end
I have a request to extract specific Invoices, over the course of an year. For target invoices i have the following information to use: Invoice Number, Invoice date, Order Ref, Value and currency. I tried extracting few months at a time, but it's too much data.
Is there a way to filter on about 200 unique Invoice No in Netsuite?
Thank you,
Daniel
You can do this with a Formula(Numeric) filter in your criteria like this:
Filter: Formula (Numeric)
Description: is greater than 0
Formula: INSTR(',SLS00000101,SLS00000102,SLS00000103,SLS00000104,SLS00000105,', ',' || {tranid} || ',')
Note that the initial string is a comma separated list of document numbers that begins and ends with a comma.
I'm not sure if there is an upper limit on formula size, but I've used this pattern to find a large number of transactions when I know the document numbers or internal IDs.