Add serialized item tracking details to a transfer - dynamics-business-central

So we are trying to shore up some gaps with implementing Microsoft Dynamics Business Central 365. Currently testing out v19 W1 on-prem. One of our use cases involves serialized items. To help shortcut the process, since the Serial No. values are unique across all items, we'd like to be able to manage items at the serial level. Rather than employ the two-step process of first providing the Item No. value and then the Serial No. value for each transaction.
Here is a basic example. In the Transfer Order page we'd like to have the user barcode scan or hand-key the Serial No. into the Item No. field for a new transfer line. Through my AL code I'd then take that Serial No. value, backtrace it to the "parent" Item No., temporarily store the Serial No. value, and then replace the field contents with the proper Item No. value. That's step one.
Step two would then involve adding a Reservation Entry line for the underlying Serial No. So that the item tracking would properly reflect the Serial No. that's associated with the transfer line. When I load up my AL extension code I encounter a quantity error, due to serialization. Although I can manually provide the same exact Item No. in that particular field and the page allows me to then proceed along in providing the quantity and whatnot.
I was advised that I need to programmatically create the transfer line, so I have attempted to define all pertinent property values. I'll paste in my AL code, along with a screen shot of the validation error. Any advice would be appreciated, as always!
tableextension 50103 "DchApi_TransferTableExt" extends "Transfer Line"
{
fields
{
modify("Item No.")
{
trigger OnBeforeValidate()
var
Rec_ILE: Record "Item Ledger Entry";
Rec_ResEnt: Record "Reservation Entry" temporary;
Rec_ResEnt_Lu: Record "Reservation Entry";
Rec_TransLine: Record "Transfer Line";
Rec_Item: Record Item;
Rec_TransHdr: Record "Transfer Header";
CreateReservEntry: Codeunit "Create Reserv. Entry";
ItemTrackingMgt: Codeunit "Item Tracking Management";
ReservStatus: Enum "Reservation Status";
CurrentSourceRowID: Text[250];
SecondSourceRowID: Text[250];
SerialNo: Code[20];
ItemNo: Code[20];
ShortDim1Code: Code[20];
ShortDim2Code: Code[20];
Description: Text[100];
GenProdPostGrp: Code[20];
InvPostGrp: Code[20];
ItemCatCode: Code[20];
InTransCode: Code[10];
TransFromCode: Code[10];
TransToCode: Code[10];
LineNo: Integer;
begin
SerialNo := Rec."Item No.";
Rec_ILE.Reset();
Rec_ILE.SetRange("Entry Type", Rec_ILE."Entry Type"::Purchase);
Rec_ILE.SetRange("Item Tracking", Rec_ILE."Item Tracking"::"Serial No.");
Rec_ILE.SetFilter("Serial No.", '%1', SerialNo);
if Rec_ILE.FindFirst() then begin
ItemNo := Rec_ILE."Item No.";
Rec_Item.Reset();
Rec_Item.SetFilter("No.", ItemNo);
if Rec_Item.FindFirst() then begin
ShortDim1Code := Rec_Item."Global Dimension 1 Code";
ShortDim2Code := Rec_Item."Global Dimension 2 Code";
Description := Rec_Item.Description;
GenProdPostGrp := Rec_Item."Gen. Prod. Posting Group";
InvPostGrp := Rec_Item."Inventory Posting Group";
ItemCatCode := Rec_Item."Item Category Code";
Rec_TransHdr.Reset();
Rec_TransHdr.SetRange("No.", Rec."Document No.");
if Rec_TransHdr.FindFirst() then begin
InTransCode := Rec_TransHdr."In-Transit Code";
TransFromCode := Rec_TransHdr."Transfer-from Code";
TransToCode := Rec_TransHdr."Transfer-to Code";
Rec_TransLine.Reset();
Rec_TransLine.SetRange("Document No.", Rec."Document No.");
if Rec_TransLine.FindLast() then
LineNo := Rec_TransLine."Line No." + 10000
else
LineNo := 10000;
Validate(Rec."Document No.");
Validate(Rec."Line No.", LineNo);
Validate(Rec."Item No.", ItemNo);
Validate(Rec."Variant Code", '');
Validate(Rec."Shortcut Dimension 1 Code", ShortDim1Code);
Validate(Rec."Shortcut Dimension 2 Code", ShortDim2Code);
Validate(Rec.Description, Description);
Validate(Rec."Gen. Prod. Posting Group", GenProdPostGrp);
Validate(Rec."Inventory Posting Group", InvPostGrp);
Validate(Rec."Item Category Code", ItemCatCode);
Validate(Rec.Quantity, 1);
Validate(Rec."Unit of Measure Code", 'PCS');
Validate(Rec."Qty. to Ship", 1);
Validate(Rec."Qty. per Unit of Measure", 1);
Validate(Rec.Status, Rec.Status::Open);
Validate(Rec."In-Transit Code", InTransCode);
Validate(Rec."Transfer-from Code", TransFromCode);
Validate(Rec."Transfer-to Code", TransToCode);
Rec.Insert();
Rec_ResEnt.Init();
Rec_ResEnt_Lu.Reset();
if Rec_ResEnt_Lu.FindLast() then
Rec_ResEnt."Entry No." := Rec_ResEnt_Lu."Entry No." + 1
else
Rec_ResEnt."Entry No." := 1;
Rec_ResEnt."Expiration Date" := Today();
Rec_ResEnt.Quantity := 1;
Rec_ResEnt."Serial No." := SerialNo;
Rec_ResEnt.Insert();
CreateReservEntry.SetDates(0D, Rec_ResEnt."Expiration Date");
CreateReservEntry.CreateReservEntryFor(Database::"Transfer Line", 0, Rec."Document No.", '', Rec."Derived From Line No.", Rec."Line No.",
Rec."Qty. per Unit of Measure", Rec_ResEnt.Quantity, Rec."Qty. per Unit of Measure" * Rec_ResEnt.Quantity, Rec_ResEnt);
CreateReservEntry.CreateEntry(Rec."Item No.", Rec."Variant Code", Rec."Transfer-from Code", Rec.Description, Rec."Receipt Date", 0D, 0, ReservStatus::Surplus);
CurrentSourceRowID := ItemTrackingMgt.ComposeRowID(5741, 0, Rec."Document No.", '', 0, Rec."Line No.");
SecondSourceRowID := ItemTrackingMgt.ComposeRowID(5741, 1, Rec."Document No.", '', 0, Rec."Line No.");
ItemTrackingMgt.SynchronizeItemTracking(CurrentSourceRowID, SecondSourceRowID, '');
end;
end;
end;
end;
}
}
}

I figured it out. I added the Serial No. value as a global variable, and broke out the reservation entry and item tracking into an OnAfterValidate() trigger. Everything works fine now. Full source code below.
tableextension 50103 "DchApi_TransferTableExt" extends "Transfer Line"
{
fields
{
modify("Item No.")
{
trigger OnBeforeValidate()
var
Rec_ILE: Record "Item Ledger Entry";
Rec_TransLine: Record "Transfer Line";
Rec_Item: Record Item;
Rec_TransHdr: Record "Transfer Header";
ItemNo: Code[20];
SerialNo: Code[20];
ShortDim1Code: Code[20];
ShortDim2Code: Code[20];
Description: Text[100];
GenProdPostGrp: Code[20];
InvPostGrp: Code[20];
ItemCatCode: Code[20];
InTransCode: Code[10];
TransFromCode: Code[10];
TransToCode: Code[10];
LineNo: Integer;
InsertResult: Boolean;
begin
SerialNo := Rec."Item No.";
Rec_ILE.Reset();
Rec_ILE.SetRange("Entry Type", Rec_ILE."Entry Type"::Purchase);
Rec_ILE.SetRange("Item Tracking", Rec_ILE."Item Tracking"::"Serial No.");
Rec_ILE.SetFilter("Serial No.", '%1', SerialNo);
if Rec_ILE.FindFirst() then begin
ItemNo := Rec_ILE."Item No.";
Rec_Item.Reset();
Rec_Item.SetFilter("No.", ItemNo);
if Rec_Item.FindFirst() then begin
ShortDim1Code := Rec_Item."Global Dimension 1 Code";
ShortDim2Code := Rec_Item."Global Dimension 2 Code";
Description := Rec_Item.Description;
GenProdPostGrp := Rec_Item."Gen. Prod. Posting Group";
InvPostGrp := Rec_Item."Inventory Posting Group";
ItemCatCode := Rec_Item."Item Category Code";
Rec_TransHdr.Reset();
Rec_TransHdr.SetRange("No.", Rec."Document No.");
if Rec_TransHdr.FindFirst() then begin
InTransCode := Rec_TransHdr."In-Transit Code";
TransFromCode := Rec_TransHdr."Transfer-from Code";
TransToCode := Rec_TransHdr."Transfer-to Code";
Rec_TransLine.Reset();
Rec_TransLine.SetRange("Document No.", Rec."Document No.");
if Rec_TransLine.FindLast() then
LineNo := Rec_TransLine."Line No." + 10000
else
LineNo := 10000;
Validate(Rec."Document No.");
Validate(Rec."Line No.", LineNo);
Validate(Rec."Item No.", ItemNo);
Validate(Rec."Description 2", SerialNo);
gblSerialNo := Rec."Description 2";
Validate(Rec."Variant Code", '');
Validate(Rec."Shortcut Dimension 1 Code", '200');
Validate(Rec."Shortcut Dimension 2 Code", '01');
Validate(Rec.Description, Description);
Validate(Rec."Gen. Prod. Posting Group", GenProdPostGrp);
Validate(Rec."Inventory Posting Group", InvPostGrp);
Validate(Rec."Item Category Code", ItemCatCode);
Validate(Rec.Quantity, 1);
Validate(Rec."Unit of Measure Code", 'PCS');
Validate(Rec."Qty. to Ship", 1);
Validate(Rec."Qty. per Unit of Measure", 1);
Validate(Rec.Status, Rec.Status::Open);
Validate(Rec."In-Transit Code", InTransCode);
Validate(Rec."Transfer-from Code", TransFromCode);
Validate(Rec."Transfer-to Code", TransToCode);
InsertResult := Rec.Insert();
Commit();
end;
end;
end;
end;
trigger OnAfterValidate()
var
Rec_ResEnt: Record "Reservation Entry" temporary;
Rec_ResEnt_Lu: Record "Reservation Entry";
CreateReservEntry: Codeunit "Create Reserv. Entry";
ItemTrackingMgt: Codeunit "Item Tracking Management";
ReservStatus: Enum "Reservation Status";
CurrentSourceRowID: Text[250];
SecondSourceRowID: Text[250];
begin
Rec_ResEnt.Init();
Rec_ResEnt_Lu.Reset();
if Rec_ResEnt_Lu.FindLast() then
Rec_ResEnt."Entry No." := Rec_ResEnt_Lu."Entry No." + 1
else
Rec_ResEnt."Entry No." := 1;
Rec_ResEnt."Expiration Date" := Today();
Rec_ResEnt.Quantity := 1;
Rec_ResEnt."Serial No." := gblSerialNo;
Rec_ResEnt.Insert();
Commit();
CreateReservEntry.SetDates(0D, Rec_ResEnt."Expiration Date");
CreateReservEntry.CreateReservEntryFor(Database::"Transfer Line", 0, Rec."Document No.", '', Rec."Derived From Line No.", Rec."Line No.",
Rec."Qty. per Unit of Measure", Rec_ResEnt.Quantity, Rec."Qty. per Unit of Measure" * Rec_ResEnt.Quantity, Rec_ResEnt);
CreateReservEntry.CreateEntry(Rec."Item No.", Rec."Variant Code", Rec."Transfer-from Code", Rec.Description, Rec."Receipt Date", 0D, 0, ReservStatus::Surplus);
CurrentSourceRowID := ItemTrackingMgt.ComposeRowID(5741, 0, Rec."Document No.", '', 0, Rec."Line No.");
SecondSourceRowID := ItemTrackingMgt.ComposeRowID(5741, 1, Rec."Document No.", '', 0, Rec."Line No.");
ItemTrackingMgt.SynchronizeItemTracking(CurrentSourceRowID, SecondSourceRowID, '');
end;
}
}
var
gblSerialNo: Code[20];
}

Related

SQLite query results limitation

I am trying to get data from SQLite database table, but i cannot get more than 50 rows. Is there a limitation of 50 rows?
My code looks like that:
unit Unit1;
interface
uses
FireDAC.Stan.Def, FireDAC.DApt, FireDAC.Phys.SQLite, FireDAC.VCLUI.Wait, FireDAC.Comp.Client, FireDAC.Stan.Async;
type
TRaportas = record
Pradzia: TDateTime;
Pabaiga: TDateTime;
Trukme: Integer;
idPriezastis: Integer;
Priezastis: string;
idVieta: Integer;
Vieta: string;
Komentaras: string;
end;
procedure TForm1.btnRaportasClick(Sender: TObject);
var
sqlConn: TFDConnection;
query: TFDQuery;
prastovuRec: array of TRaportas;
i: Integer;
begin
dbVieta := edt2.Text;
sqlConn := TFDConnection.Create(nil);
//sqlConn.Connected := False;
sqlConn.DriverName := 'SQLITE';
sqlConn.Params.Values['DataBase'] := dbVieta;
query := TFDQuery.Create(nil);
query.Connection := sqlConn;
query.SQL.Text := 'SELECT * FROM Prastovos WHERE ID >= :_ID';
query.ParamByName('_ID').Value := StrToIntDef(edt3.Text, 656);
sqlConn.Open();
query.Open();
SetLength(prastovuRec, query.RowsAffected);
edt4.Text := IntToStr(query.RowsAffected);
for i := 0 to query.RowsAffected - 1 do
begin
with mRaportas do
begin
Pradzia := query.FieldByName('Pradzia').AsDateTime;
Pabaiga := query.FieldByName('Pabaiga').AsDateTime;
Trukme := query.FieldByName('Trukme').AsInteger;
idPriezastis := query.FieldByName('IDpriezastis').AsInteger;
Priezastis := query.FieldByName('Priezastis').AsString;
idVieta := query.FieldByName('IDvieta').AsInteger;
Vieta := query.FieldByName('Vieta').AsString;
Komentaras := query.FieldByName('Komentaras').AsString;
end;
prastovuRec[i] := mRaportas;
query.Next;
end;
query.Close;
query.DisposeOf;
sqlConn.Close;
sqlConn.Free;
end;
There is a lot of mistakes and misunderstandings inyour code. To simplify, I'm just going to fix your code to make it work. Study the differences yourself.
unit Unit1;
interface
uses
FireDAC.Stan.Def, FireDAC.DApt, FireDAC.Phys.SQLite, FireDAC.VCLUI.Wait, FireDAC.Comp.Client,
FireDAC.Stan.Async, FireDAC.Stan.Option;
type
TRaportas = record
Pradzia: TDateTime;
Pabaiga: TDateTime;
Trukme: Integer;
idPriezastis: Integer;
Priezastis: string;
idVieta: Integer;
Vieta: string;
Komentaras: string;
end;
var
prastovuRec: array of TRaportas;
procedure TForm1.Button7Click(Sender: TObject);
var
sqlConn: TFDConnection;
query: TFDQuery;
mRaportas: TRaportas;
i: Integer;
begin
sqlConn := TFDConnection.Create(nil);
query := TFDQuery.Create(nil);
try
sqlConn.DriverName := 'SQLITE';
sqlConn.Params.Values['DataBase'] := edt2.Text;
query.Connection := sqlConn;
query.FetchOptions.Mode := fmAll; // essential if you want to use RecordCount
query.SQL.Text := 'SELECT * FROM Prastovos WHERE ID >= :_ID';
query.ParamByName('_ID').Value := StrToIntDef(edt3.Text, 656);
query.Open();
edt4.Text := IntToStr(query.RecordCount);
SetLength(prastovuRec, query.RecordCount);
i := 0;
while not query.Eof do
begin
mRaportas := Default(TRaportas); // not necessary if you assign all record fields
mRaportas.Pradzia := query.FieldByName('Pradzia').AsDateTime;
mRaportas.Pabaiga := query.FieldByName('Pabaiga').AsDateTime;
mRaportas.Trukme := query.FieldByName('Trukme').AsInteger;
mRaportas.idPriezastis := query.FieldByName('IDpriezastis').AsInteger;
mRaportas.Priezastis := query.FieldByName('Priezastis').AsString;
mRaportas.idVieta := query.FieldByName('IDvieta').AsInteger;
mRaportas.Vieta := query.FieldByName('Vieta').AsString;
mRaportas.Komentaras := query.FieldByName('Komentaras').AsString;
prastovuRec[i] := mRaportas;
Inc(i);
query.Next;
end;
query.Close;
finally
query.Free;
sqlConn.Free;
end;
end;
No, there is no special limit.
But you have a WHERE clause in your query. It is likely that which limits the result set. Check the value you use.

IdCookieManager cut part of server cookie which contains quotechar

There's a problem with IdCookieManager. When server returns cookie where value contains ", it recognize first occurrence of " as end of value. You can reproduce it easily with next code:
procedure TSomeObject.Test;
var
HTTP: TIdHTTP;
Cookie: TIdCookieManager;
i: Integer;
begin
HTTP := TIdHTTP.Create(nil);
Cookie := TIdCookieManager.Create(HTTP);
HTTP.CookieManager := Cookie;
HTTP.HandleRedirects := True;
HTTP.Get('http://httpbin.org/cookies/set?test_cookie1=' +
TIdURI.ParamsEncode('{"key": 123}') + '&test_cookie2=&test_cookie3=value');
for i := 0 to Cookie.CookieCollection.Count - 1 do
Form1.Memo1.Lines.Add(Cookie.CookieCollection[i].CookieName + ' = ' +
Cookie.CookieCollection[i].Value);
HTTP.Free;
end;
Digging into Indy sources I found that problem is in TIdCookie.ParseServerCookie(). It uses IdGlobal.Fetch() to extract value between quotes and ... it does what it does.
Would you recommend me how to let it parse whole value?
There's no neat solution (at least I haven't found one) ,so I've added OnHeadersAvailable event listener to TIdHTTP which rewrites cookie value after TIdCookie.ParseServerCookie() executed and replace previously parsed cookies by adding new cookie with same name to TIdCookieManager.
Code (DO NOT USE THIS HANDLER, THERE'S ONE MORE BELOW):
procedure TSomeObject.FixCookies(Sender: TObject; AHeaders: TIdHeaderList;
var VContinue: Boolean);
const
CookieDelimiter = ';';
QuoteChar = '"';
SpaceChar = ' ';
var
RawHeader, RawCookie, RawCookieValue: string;
i, CookieDelimiterPos, CookieNamePos, CookieValuePos: Integer;
Cookie: TIdCookie;
begin
for i := 0 to AHeaders.Count - 1 do
begin
RawHeader := AHeaders[i];
if Pos('Set-Cookie', RawHeader) = 1 then // starts with "Set-Cookie"
begin
for CookieNamePos := Length('Set-Cookie') + 2 to Length(RawHeader) do
if RawHeader[CookieNamePos] <> SpaceChar then
Break;
RawCookie := Copy(RawHeader, CookieNamePos,
Length(RawHeader) - CookieNamePos + 1);
CookieDelimiterPos := Pos(CookieDelimiter, RawCookie);
CookieValuePos := Pos('=', RawCookie);
if (CookieDelimiterPos > 0) and (CookieValuePos > 0) then
begin
RawCookieValue := Copy(RawCookie, CookieValuePos + 1,
CookieDelimiterPos - CookieValuePos - 1);
if (Length(RawCookieValue) > 0) and (RawCookieValue[1] = QuoteChar) and
(RawCookieValue[Length(RawCookieValue)] = QuoteChar) then
RawCookieValue := Copy(RawCookieValue, 2, Length(RawCookieValue) - 2);
with (Sender as TIdHTTP) do
begin
Cookie := TIdCookie.Create(nil);
if Cookie.ParseServerCookie(RawCookie, URL) then
begin
Cookie.Value := RawCookieValue;
CookieManager.CookieCollection.AddCookie(Cookie, URL);
end
else
Cookie.Free;
end;
end;
end;
end;
VContinue := True;
end;
Usage:
HTTP.OnHeadersAvailable := FixCookies; // Set it after object initialization
P.S. I haven't used delphi for long time, so I'm sure that code isn't perfect and lot of improvements could be done (welcome to comments), but it works.
I have no clue how it happened but using this event handler somehow brakes TIdCookieManager and Cookie.CookieCollection.Count returns 0 while there're cookies in collection which successfully added in further requests..
I have no mood to spend more time digging into Indy sources so I changed handler to modify existing cookies instead of replacing them. It doesn't brake anything (probably):
procedure TSomeObject.FixCookies(Sender: TObject; AHeaders: TIdHeaderList;
var VContinue: Boolean);
const
CookieDelimiter = ';';
QuoteChar = '"';
SpaceChar = ' ';
var
RawHeader, RawCookie, RawCookieValue, RawCookieName: string;
i, CookieDelimiterPos, CookieNamePos, CookieValuePos, CookieIndex: Integer;
begin
for i := 0 to AHeaders.Count - 1 do
begin
RawHeader := AHeaders[i];
if Pos('Set-Cookie', RawHeader) = 1 then // starts with "Set-Cookie"
begin
for CookieNamePos := Length('Set-Cookie') + 2 to Length(RawHeader) do
if RawHeader[CookieNamePos] <> SpaceChar then
Break;
RawCookie := Copy(RawHeader, CookieNamePos,
Length(RawHeader) - CookieNamePos + 1);
CookieDelimiterPos := Pos(CookieDelimiter, RawCookie);
CookieValuePos := Pos('=', RawCookie);
if (CookieDelimiterPos > 0) and (CookieValuePos > 0) then
begin
RawCookieName := Copy(RawCookie, 1, CookieValuePos - 1);
RawCookieValue := Copy(RawCookie, CookieValuePos + 1,
CookieDelimiterPos - CookieValuePos - 1);
if (Length(RawCookieValue) > 0) and (RawCookieValue[1] = QuoteChar) and
(RawCookieValue[Length(RawCookieValue)] = QuoteChar) then
RawCookieValue := Copy(RawCookieValue, 2, Length(RawCookieValue) - 2);
with (Sender as TIdHTTP) do
begin
CookieIndex := CookieManager.CookieCollection.GetCookieIndex
(RawCookieName);
if CookieIndex >= 0 then
CookieManager.CookieCollection[CookieIndex].Value := RawCookieValue
end;
end;
end;
end;
VContinue := True;
end;
P.S. Are there any Indy developers? I just a bit confused how does first handler let CookieCollection to return zero.

decoding clob in oracle and then encoding this blob in classic asp does not give the same clob

I have two column in a table whose datatype are clob and blob . I insert a clob data in clob column and insert this data to blob data by decoding this clob in oracle by the following code :
function decode_base64(p_clob_in in clob) return blob is
v_blob blob;
v_result blob;
v_offset integer;
v_buffer_size binary_integer := 48;
v_buffer_varchar varchar2(48);
v_buffer_raw raw(48);
begin
if p_clob_in is null then
return null;
end if;
dbms_lob.createtemporary(v_blob, true);
v_offset := 1;
for i in 1 .. ceil(dbms_lob.getlength(p_clob_in) / v_buffer_size) loop
dbms_lob.read(p_clob_in, v_buffer_size, v_offset, v_buffer_varchar);
v_buffer_raw := utl_raw.cast_to_raw(v_buffer_varchar);
v_buffer_raw := utl_encode.base64_decode(v_buffer_raw);
dbms_lob.writeappend(v_blob, utl_raw.length(v_buffer_raw), v_buffer_raw);
v_offset := v_offset + v_buffer_size;
end loop;
v_result := v_blob;
dbms_lob.freetemporary(v_blob);
return v_result;
end decode_base64;
Then I get this blob data in asp.net by the following code :
strSQL ="SELECT BIO_DATA FingerData , DATA_LENGTH len_of_data , SERIAL_NO sl_no FROM FP_BIOMETRIC_DATA WHERE CUST_NO =" & trim(Request("name")) & " "
Set objExec = Conn.Execute(strSQL)
fingerData1 = objExec("FingerData")
Then I am encoding this data into base64 by the following code :
Function Base64Encode(sText)
Dim oXML, oNode
Set oXML = CreateObject("Msxml2.DOMDocument.3.0")
Set oNode = oXML.CreateElement("base64")
oNode.dataType = "bin.base64"
oNode.nodeTypedValue =sText
Base64Encode = oNode.text
Set oNode = Nothing
Set oXML = Nothing
End Function
Then I am trying to compare this data and clob data in oracle database by this website . This website tells that the two data are different . Why ? Where is the error ? How can I get blob data by decoding a clob data in oracle ?
I think the problem is at this line
dbms_lob.read(p_clob_in, v_buffer_size, v_offset, v_buffer_varchar);
v_buffer_size is fixed at 48 characters, however your BASE64 string may contain NEW_LINE characters which are ignored for decoding but they are counted for v_buffer_size.
You have to remove all NEW_LINE characters before you read your buffer or increase value of v_buffer_size by number of NEW_LINE characters in substring.
Try this one:
CREATE OR REPLACE FUNCTION DecodeBASE64(InBase64Char IN OUT NOCOPY CLOB) RETURN BLOB IS
res BLOB;
clob_trim CLOB;
dest_offset INTEGER := 1;
src_offset INTEGER := 1;
read_offset INTEGER := 1;
ClobLen INTEGER;
amount INTEGER := 1440; -- must be a whole multiple of 4
buffer RAW(1440);
stringBuffer VARCHAR2(1440);
BEGIN
IF DBMS_LOB.GETLENGTH(InBase64Char) IS NULL THEN
RETURN NULL;
END IF;
-- Remove all NEW_LINE from base64 string
ClobLen := DBMS_LOB.GETLENGTH(InBase64Char);
DBMS_LOB.CREATETEMPORARY(clob_trim, TRUE);
LOOP
EXIT WHEN read_offset > ClobLen;
stringBuffer := REPLACE(REPLACE(DBMS_LOB.SUBSTR(InBase64Char, amount, read_offset), CHR(13), NULL), CHR(10), NULL);
DBMS_LOB.WRITEAPPEND(clob_trim, LENGTH(stringBuffer), stringBuffer);
read_offset := read_offset + amount;
END LOOP;
read_offset := 1;
ClobLen := DBMS_LOB.GETLENGTH(clob_trim);
DBMS_LOB.CREATETEMPORARY(res, TRUE);
LOOP
EXIT WHEN read_offset > ClobLen;
buffer := UTL_ENCODE.BASE64_DECODE(UTL_RAW.CAST_TO_RAW(DBMS_LOB.SUBSTR(clob_trim, amount, read_offset)));
DBMS_LOB.WRITEAPPEND(res, DBMS_LOB.GETLENGTH(buffer), buffer);
read_offset := read_offset + amount;
END LOOP;
RETURN res;
END DecodeBASE64;

How to initialize object of record as array in plsql?

I have following pl/SQL block for getting records from record type,and i am using object of record as array.It gives wrong number or types of argument error where i initialize array.kindly give me written solution for initializing object of record as array.
DECLARE
TYPE loc IS RECORD (
loc_id number(4),
str_add VARCHAR2(40),
p_code VARCHAR2(12),
city VARCHAR2(30),
st_pro VARCHAR2(25),
c_id CHAR(2),
Oper varchar2(1));
--names namesarray;
--loc_rec loc IS VARRAY(8);
type loc_rec IS VARRAY(8) OF loc;
total number(4);
tot number(3);
loc_id1 number(4);
i number(2);
begin
i:=1;
loc_rec(1).loc_id:=1;
loc_rec(1).str_add:='chikhli';
loc_rec(1).p_code:='396521';
loc_rec(1).city:='chikhli';
loc_rec(1).st_pro:='Gujarat';
loc_rec(1).c_id:='G1';
loc_rec(1).Oper:='I';
loc_rec(2).loc_id:=2;
loc_rec(2).str_add:='chikhli';
loc_rec(2).p_code:='396521';
loc_rec(2).city:='chikhli';
loc_rec(2).st_pro:='Gujarat';
loc_rec(2).c_id:='G1';
loc_rec(2).Oper:='U';
loc_rec(3).loc_id:=3;
loc_rec(3).str_add:='chikhli';
loc_rec(3).p_code:='396521';
loc_rec(3).city:='chikhli';
loc_rec(3).st_pro:='Gujarat';
loc_rec(3).c_id:='G1';
loc_rec(3).Oper:='D';
--names := --namesarray(loc_rec.loc_id,loc_rec.str_add,loc_rec.p_code,
--loc_rec.city,loc_rec.st_pro,loc_rec.c_id,loc_rec.Oper);
LOOP
--total := names.count;
if loc_rec(i).Oper='I' then
insert into locations values(loc_rec(i).loc_id,
loc_rec(i).str_add,
loc_rec(i).p_code,
loc_rec(i).city,
loc_rec(i).st_pro,
loc_rec(i).c_id
);
dbms_output.put_line('Record Inserted Successfully');
elsif loc_rec(i).Oper='D' then
delete from locations where location_id=loc_rec(i).loc_id;
dbms_output.put_line('Record deleted Successfully');
if(sql%NOTFOUND) then
dbms_output.put_line('Input location ID Not Found');
end if;
elsif loc_rec(i).Oper='U' then
update locations
set
street_address=loc_rec(i).str_add,
postal_code=loc_rec(i).p_code,
city=loc_rec(i).city,
state_province=loc_rec(i).st_pro,
country_id=loc_rec(i).c_id
where location_id=loc_rec(i).loc_id;
dbms_outt.put_line('Record Updated Successfully');
if(sql%NOTFOUND) then
insert into locations values(loc_rec(i).loc_id,
loc_rec(i).str_add,
loc_rec(i).p_code,
loc_rec(i).city,
loc_rec(i).st_pro,
loc_rec(i).c_id
);
end if;
else
dbms_output.put_line('Kindly give proper Input: I:insert U:update D:delete');
end if;
commit;
i:=i+1;
END LOOP;
end;
/
There are several things wrong with your code.
You haven't declared a variable to hold your array
You haven't initialized the collection
You haven't extended the collection to hold a new row
dbms_outt.put_line... typo; I think you mean dbms_output.put_line
You're not looping through the collection correctly - you've more or less coded an infinite loop, except eventually you'll run out of items in your collection and will end up with a subscript out of range error eventually.
Your code should be something like:
declare
type loc is record (loc_id number (4),
str_add varchar2 (40),
p_code varchar2 (12),
city varchar2 (30),
st_pro varchar2 (25),
c_id char (2),
oper varchar2 (1));
--names namesarray;
--loc_rec loc IS VARRAY(8);
type loc_varray is varray (8) of loc;
total number (4);
tot number (3);
loc_id1 number (4);
loc_rec loc_varray := loc_varray();
begin
loc_rec.extend;
loc_rec (1).loc_id := 1;
loc_rec (1).str_add := 'chikhli';
loc_rec (1).p_code := '396521';
loc_rec (1).city := 'chikhli';
loc_rec (1).st_pro := 'Gujarat';
loc_rec (1).c_id := 'G1';
loc_rec (1).oper := 'I';
loc_rec.extend;
loc_rec (2).loc_id := 2;
loc_rec (2).str_add := 'chikhli';
loc_rec (2).p_code := '396521';
loc_rec (2).city := 'chikhli';
loc_rec (2).st_pro := 'Gujarat';
loc_rec (2).c_id := 'G1';
loc_rec (2).oper := 'U';
loc_rec.extend;
loc_rec (3).loc_id := 3;
loc_rec (3).str_add := 'chikhli';
loc_rec (3).p_code := '396521';
loc_rec (3).city := 'chikhli';
loc_rec (3).st_pro := 'Gujarat';
loc_rec (3).c_id := 'G1';
loc_rec (3).oper := 'D';
--names := --namesarray(loc_rec.loc_id,loc_rec.str_add,loc_rec.p_code,
--loc_rec.city,loc_rec.st_pro,loc_rec.c_id,loc_rec.Oper);
for i in loc_rec.first..loc_rec.last
loop
--total := names.count;
if loc_rec (i).oper = 'I'
then
insert Into Locations
Values (Loc_Rec (I).Loc_Id,
Loc_Rec (I).Str_Add,
Loc_Rec (I).P_Code,
Loc_Rec (I).City,
Loc_Rec (I).St_Pro,
Loc_Rec (I).C_Id);
dbms_output.put_line ('Record Inserted Successfully');
elsif loc_rec (i).oper = 'D'
then
delete from locations
where location_id = loc_rec (i).loc_id;
dbms_output.put_line ('Record deleted Successfully');
if (sql%notfound)
then
dbms_output.put_line ('Input location ID Not Found');
end if;
elsif loc_rec (i).oper = 'U'
then
UPDATE locations
SET street_address = loc_rec (i).str_add,
postal_code = loc_rec (i).p_code,
city = loc_rec (i).city,
state_province = loc_rec (i).st_pro,
country_id = loc_rec (i).c_id
WHERE location_id = loc_rec (i).loc_id;
dbms_output.put_line ('Record Updated Successfully');
if (sql%notfound)
then
insert into locations
values (loc_rec (i).loc_id,
loc_rec (i).str_add,
loc_rec (i).p_code,
loc_rec (i).city,
loc_rec (i).st_pro,
loc_rec (i).c_id);
end if;
else
dbms_output.put_line (
'Kindly give proper Input: I:insert U:update D:delete');
end if;
commit;
end loop;
end;
/

How to compare a negative number in pl/sql if statement

Here is the complete function
Function GetStatus_TecTrac( inquiry_id IN number )
RETURN VARCHAR2
IS
status VARCHAR2(12);
deptId number;
endDate date;
BEGIN
select DEPT_ID, END_DATE into deptId , endDate from (
select DEPT_ID, END_DATE
from TB_PROJECT_TRACKING_DURATION tp
where tp.INQUIRY_ID = inquiry_id
order by START_DATE desc
)
where rownum = 1;
if (deptId = -61 and endDate is not null) then
status := 'closed';
else
status := 'open';
end if;
RETURN status;
END;
It always returns the status as "closed" on each inquiry_id whatever the value of deptId and endDate is.
Help me to solve this.
Thanks in advance.
Farhan
Works for me. How did you test?
SQL> declare
deptid number := 44;
enddate date := sysdate;
status varchar2(10);
begin
if (deptId = -61 and endDate is not null) then
status := 'closed';
else
status := 'open';
end if;
dbms_output.put_line(status);
end;
/
open
PL/SQL procedure successfully completed.

Resources