Commit 0128c655 by Cam Hayes

version 0.9.13.8 deployed on kgOrdersTest

parent 1fc480c1
...@@ -19,7 +19,7 @@ type ...@@ -19,7 +19,7 @@ type
FUnauthorizedAccessProc: TUnauthorizedAccessProc; FUnauthorizedAccessProc: TUnauthorizedAccessProc;
public public
const clientVersion = '0.9.13.7'; const clientVersion = '0.9.13.8';
procedure InitApp(SuccessProc: TSuccessProc; procedure InitApp(SuccessProc: TSuccessProc;
UnauthorizedAccessProc: TUnauthorizedAccessProc); UnauthorizedAccessProc: TUnauthorizedAccessProc);
procedure SetClientConfig(Callback: TVersionCheckCallback); procedure SetClientConfig(Callback: TVersionCheckCallback);
......
...@@ -132,18 +132,18 @@ object FViewMain: TFViewMain ...@@ -132,18 +132,18 @@ object FViewMain: TFViewMain
HeightPercent = 100.000000000000000000 HeightPercent = 100.000000000000000000
WidthPercent = 100.000000000000000000 WidthPercent = 100.000000000000000000
end end
object lblLinkToQB: TWebLabel object lblQBInfo: TWebLabel
Left = 538 Left = 552
Top = 128 Top = 126
Width = 49 Width = 36
Height = 14 Height = 14
Caption = 'Link to QB' Caption = 'QB Info'
ElementID = 'dropdown.menu.linktoqb' ElementID = 'dropdown.menu.linktoqb'
ElementFont = efCSS ElementFont = efCSS
HeightStyle = ssAuto HeightStyle = ssAuto
HeightPercent = 100.000000000000000000 HeightPercent = 100.000000000000000000
WidthPercent = 100.000000000000000000 WidthPercent = 100.000000000000000000
OnClick = lblLinkToQBClick OnClick = lblQBInfoClick
end end
object WebPanel1: TWebPanel object WebPanel1: TWebPanel
Left = 77 Left = 77
......
...@@ -33,7 +33,7 @@ ...@@ -33,7 +33,7 @@
<a class="dropdown-item" id="dropdown.menu.users" href="#"><i class="fas fa-address-book fa-fw"></i><span> Users</span></a> <a class="dropdown-item" id="dropdown.menu.users" href="#"><i class="fas fa-address-book fa-fw"></i><span> Users</span></a>
</li> </li>
<li> <li>
<a class="dropdown-item" id="dropdown.menu.linktoqb" href="#"><i class="fas fa-book fa-fw"></i><span> Link to QB</span></a> <a class="dropdown-item" id="dropdown.menu.linktoqb" href="#"><i class="fas fa-book fa-fw"></i><span> QB Info</span></a>
</li> </li>
<li> <li>
<hr class="dropdown-divider"> <hr class="dropdown-divider">
......
...@@ -6,7 +6,8 @@ uses ...@@ -6,7 +6,8 @@ uses
System.SysUtils, System.Classes, JS, Web, WEBLib.Graphics, WEBLib.Controls, System.SysUtils, System.Classes, JS, Web, WEBLib.Graphics, WEBLib.Controls,
WEBLib.Forms, WEBLib.Dialogs, WEBLib.ExtCtrls, Vcl.Controls, Vcl.StdCtrls, WEBLib.Forms, WEBLib.Dialogs, WEBLib.ExtCtrls, Vcl.Controls, Vcl.StdCtrls,
WEBLib.StdCtrls, Data.DB, XData.Web.JsonDataset, XData.Web.Dataset, WEBLib.StdCtrls, Data.DB, XData.Web.JsonDataset, XData.Web.Dataset,
App.Types, ConnectionModule, XData.Web.Client, WEBLib.Menus, Utils, System.IOUtils; App.Types, ConnectionModule, XData.Web.Client, WEBLib.Menus, Utils, System.IOUtils,
Vcl.Forms;
type type
TFViewMain = class(TWebForm) TFViewMain = class(TWebForm)
...@@ -24,7 +25,7 @@ type ...@@ -24,7 +25,7 @@ type
lblorders: TWebLabel; lblorders: TWebLabel;
lblCustomers: TWebLabel; lblCustomers: TWebLabel;
lblVersion: TWebLabel; lblVersion: TWebLabel;
lblLinkToQB: TWebLabel; lblQBInfo: TWebLabel;
procedure WebFormCreate(Sender: TObject); procedure WebFormCreate(Sender: TObject);
procedure mnuLogoutClick(Sender: TObject); procedure mnuLogoutClick(Sender: TObject);
procedure wllblUserProfileClick(Sender: TObject); procedure wllblUserProfileClick(Sender: TObject);
...@@ -34,7 +35,7 @@ type ...@@ -34,7 +35,7 @@ type
procedure lblUsersClick(Sender: TObject); procedure lblUsersClick(Sender: TObject);
procedure lblordersClick(Sender: TObject); procedure lblordersClick(Sender: TObject);
procedure lblCustomersClick(Sender: TObject); procedure lblCustomersClick(Sender: TObject);
procedure lblLinkToQBClick(Sender: TObject); procedure lblQBInfoClick(Sender: TObject);
{ Private declarations } { Private declarations }
private private
FUserInfo: string; FUserInfo: string;
...@@ -83,7 +84,8 @@ uses ...@@ -83,7 +84,8 @@ uses
View.OrderEntryCuttingDie, View.OrderEntryCuttingDie,
View.OrderEntryWeb, View.OrderEntryWeb,
View.Customers, View.Customers,
View.Customer.Add; View.Customer.Add,
View.QBInfo;
{$R *.dfm} {$R *.dfm}
...@@ -101,7 +103,7 @@ begin ...@@ -101,7 +103,7 @@ begin
if (not (JS.toString(AuthService.TokenPayload.Properties['user_access_type']) = 'ADMIN')) then if (not (JS.toString(AuthService.TokenPayload.Properties['user_access_type']) = 'ADMIN')) then
begin begin
lblUsers.enabled := false; lblUsers.enabled := false;
lblLinkToQB.Enabled := false; lblQBInfo.Enabled := false;
lblCustomers.Enabled := false; lblCustomers.Enabled := false;
end; end;
lblAppTitle.Caption := 'Koehler-Gibson Orders'; lblAppTitle.Caption := 'Koehler-Gibson Orders';
...@@ -178,7 +180,7 @@ begin ...@@ -178,7 +180,7 @@ begin
ShowToast('Please Save or Cancel your changes', 'danger'); ShowToast('Please Save or Cancel your changes', 'danger');
end; end;
procedure TFViewMain.lblLinkToQBClick(Sender: TObject); procedure TFViewMain.lblQBInfoClick(Sender: TObject);
var var
qblink, redirectUri: string; qblink, redirectUri: string;
qbWindow: TJSWindow; qbWindow: TJSWindow;
...@@ -187,31 +189,34 @@ begin ...@@ -187,31 +189,34 @@ begin
end; end;
procedure TFViewMain.ShowQBInfoForm(); procedure TFViewMain.ShowQBInfoForm();
//var var
// newform: TFQBInfo; newform: TFQBInfo;
begin begin
// newform := TFQBInfo.CreateNew; newform := TFQBInfo.CreateNew;
//
// newform.Caption := 'Quickbooks information'; newform.Caption := 'Quickbooks information';
// newForm.Popup := True;
// // used to manage Back button handling to close subform newForm.Border := fbDialog;
// window.location.hash := 'subform'; newForm.Position := poScreenCenter;
//
// newform.ShowModal( // used to manage Back button handling to close subform
// procedure(AValue: TModalResult) window.location.hash := 'subform';
// begin
//// if newform.confirm then newform.ShowModal(
//// begin procedure(AValue: TModalResult)
//// if newform.cbCorrugatedPlate.Checked then begin
//// orderType := 'corrugated' // if newform.confirm then
//// else if newform.cbWebPlate.Checked then // begin
//// orderType := 'web' // if newform.cbCorrugatedPlate.Checked then
//// else // orderType := 'corrugated'
//// orderType := 'cutting'; // else if newform.cbWebPlate.Checked then
//// orderEntry('', newForm.DBID, 'ADD', orderType); // orderType := 'web'
//// end; // else
// end // orderType := 'cutting';
// ); // orderEntry('', newForm.DBID, 'ADD', orderType);
// end;
end
);
end; end;
procedure TFViewMain.setActive(page: string); procedure TFViewMain.setActive(page: string);
......
object FQBInfo: TFQBInfo object FQBInfo: TFQBInfo
Width = 640 Width = 449
Height = 208 Height = 157
object WebLabel1: TWebLabel OnCreate = WebFormCreate
OnShow = WebFormShow
object lblConnected: TWebLabel
Left = 16 Left = 16
Top = 24 Top = 8
Width = 268 Width = 7
Height = 32 Height = 32
Caption = 'Quickbooks is not linked.'
Font.Charset = DEFAULT_CHARSET Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText Font.Color = clWindowText
Font.Height = -24 Font.Height = -24
...@@ -18,7 +19,7 @@ object FQBInfo: TFQBInfo ...@@ -18,7 +19,7 @@ object FQBInfo: TFQBInfo
end end
object lblCompanyName: TWebLabel object lblCompanyName: TWebLabel
Left = 16 Left = 16
Top = 68 Top = 46
Width = 120 Width = 120
Height = 21 Height = 21
Caption = 'Company Name: ' Caption = 'Company Name: '
...@@ -33,7 +34,7 @@ object FQBInfo: TFQBInfo ...@@ -33,7 +34,7 @@ object FQBInfo: TFQBInfo
end end
object lblCompanyID: TWebLabel object lblCompanyID: TWebLabel
Left = 16 Left = 16
Top = 98 Top = 76
Width = 93 Width = 93
Height = 21 Height = 21
Caption = 'Company ID: ' Caption = 'Company ID: '
...@@ -47,8 +48,8 @@ object FQBInfo: TFQBInfo ...@@ -47,8 +48,8 @@ object FQBInfo: TFQBInfo
WidthPercent = 100.000000000000000000 WidthPercent = 100.000000000000000000
end end
object WebButton1: TWebButton object WebButton1: TWebButton
Left = 16 Left = 13
Top = 125 Top = 107
Width = 96 Width = 96
Height = 25 Height = 25
Caption = 'Link to QB' Caption = 'Link to QB'
...@@ -57,4 +58,9 @@ object FQBInfo: TFQBInfo ...@@ -57,4 +58,9 @@ object FQBInfo: TFQBInfo
WidthPercent = 100.000000000000000000 WidthPercent = 100.000000000000000000
OnClick = WebButton1Click OnClick = WebButton1Click
end end
object XDataWebClient1: TXDataWebClient
Connection = DMConnection.ApiConnection
Left = 376
Top = 63
end
end end
...@@ -4,17 +4,23 @@ interface ...@@ -4,17 +4,23 @@ interface
uses uses
System.SysUtils, System.Classes, JS, Web, WEBLib.Graphics, WEBLib.Controls, System.SysUtils, System.Classes, JS, Web, WEBLib.Graphics, WEBLib.Controls,
WEBLib.Forms, WEBLib.Dialogs, Vcl.Controls, Vcl.StdCtrls, WEBLib.StdCtrls; WEBLib.Forms, WEBLib.Dialogs, Vcl.Controls, Vcl.StdCtrls, WEBLib.StdCtrls,
XData.Web.Client, ConnectionModule;
type type
TFQBInfo = class(TWebForm) TFQBInfo = class(TWebForm)
WebLabel1: TWebLabel; lblConnected: TWebLabel;
lblCompanyName: TWebLabel; lblCompanyName: TWebLabel;
lblCompanyID: TWebLabel; lblCompanyID: TWebLabel;
WebButton1: TWebButton; WebButton1: TWebButton;
XDataWebClient1: TXDataWebClient;
procedure WebButton1Click(Sender: TObject); procedure WebButton1Click(Sender: TObject);
procedure WebFormCreate(Sender: TObject);
procedure WebFormShow(Sender: TObject);
private private
{ Private declarations } { Private declarations }
ClientID: string;
[async] procedure GetQBInfo();
public public
{ Public declarations } { Public declarations }
end; end;
...@@ -26,23 +32,69 @@ implementation ...@@ -26,23 +32,69 @@ implementation
{$R *.dfm} {$R *.dfm}
uses View.Main, Utils;
procedure TFQBInfo.WebButton1Click(Sender: TObject); procedure TFQBInfo.WebButton1Click(Sender: TObject);
var var
qblink, redirectUri: string; qblink, redirectUri: string;
qbWindow: TJSWindow; qbWindow: TJSWindow;
begin begin
// qblink := 'https://appcenter.intuit.com/connect/oauth2'; qblink := 'https://appcenter.intuit.com/connect/oauth2';
// //qblink := qblink + '?client_id=ABYqlDx1EsacZYXvHIJ7RDB7zmnQdwABU3fwQLIZPmBgU0VW1P'; qblink := qblink + '?client_id=' + ClientID;
// qblink := qblink + '?client_id=ABXLUuBrwvodIZMtaPcBg9acYBPafUxP80qk5aQ9HOAw1i2W3s'; qblink := qblink + '&response_type=code';
// qblink := qblink + '&response_type=code'; qblink := qblink + '&scope=com.intuit.quickbooks.accounting';
// qblink := qblink + '&scope=com.intuit.quickbooks.accounting'; qblink := qblink + '&state=7';
// qblink := qblink + '&state=7'; redirectUri := DMConnection.AuthConnection.URL + 'AuthService/QBAuthorize';
// //redirectUri := DMConnection.AuthConnection.URL + 'AuthService/QBAuthorize'; qblink := qblink + '&redirect_uri=' + RedirectUri;
// qblink := qblink + '&redirect_uri=' + RedirectUri; console.log(redirectUri);
// console.log(redirectUri); console.log(qbLink);
// qbWindow := window.open('', '_blank'); qbWindow := window.open('', '_blank');
// if Assigned(qbWindow) then if Assigned(qbWindow) then
// qbWindow.location.href := qbLink; qbWindow.location.href := qbLink;
Close();
end;
procedure TFQBInfo.WebFormCreate(Sender: TObject);
begin
if not DMConnection.ApiConnection.Connected then
begin
DMConnection.ApiConnection.OpenAsync;
console.log('report requirements connection open')
end;
end;
procedure TFQBInfo.WebFormShow(Sender: TObject);
begin
GetQBInfo();
end; end;
procedure TFQBInfo.GetQBInfo();
// retrieves customer list from server
var
xdcResponse: TXDataClientResponse;
QBInfo: TJSObject;
begin
try
Utils.ShowSpinner('spinner');
xdcResponse := await(XDataWebClient1.RawInvokeAsync('ILookupService.getQBInfo', []));
QBInfo := TJSObject(xdcResponse.Result);
ClientID := String(QBInfo['clientID']);
console.log(QBInfo);
if boolean(QBInfo['connected']) then
begin
lblCompanyName.Caption := lblCompanyName.Caption + String(QBInfo['CompanyName']);
lblCompanyID.Caption := lblCompanyID.Caption + String(QBInfo['CompanyID']);
lblConnected.Caption := 'QuickBooks is connected.'
end
else
lblConnected.Caption := 'Quickbooks is not connected.';
Utils.HideSpinner('spinner');
except
on E: EXDataClientRequestException do
Utils.ShowErrorModal(E.ErrorResult.ErrorMessage);
end;
end;
end. end.
{ {
"AuthUrl" : "http://localhost:2004/kgOrders/auth/", "AuthUrl" : "http://localhost:2004/kgOrders/auth/",
"ApiUrl" : "http://localhost:2004/kgOrders/api/", "ApiUrl" : "http://localhost:2004/kgOrders/api/"
} }
...@@ -29,7 +29,8 @@ uses ...@@ -29,7 +29,8 @@ uses
View.Address.Add in 'View.Address.Add.pas' {FViewAddAddress: TWebForm} {*.html}, View.Address.Add in 'View.Address.Add.pas' {FViewAddAddress: TWebForm} {*.html},
View.Customer.Select in 'View.Customer.Select.pas' {FSelectCustomer: TWebForm} {*.html}, View.Customer.Select in 'View.Customer.Select.pas' {FSelectCustomer: TWebForm} {*.html},
Utils in 'Utils.pas', Utils in 'Utils.pas',
View.Item.Add in 'View.Item.Add.pas' {fViewAddItem: TWebForm} {*.html}; View.Item.Add in 'View.Item.Add.pas' {fViewAddItem: TWebForm} {*.html},
View.QBInfo in 'View.QBInfo.pas' {FQBInfo: TWebForm} {*.html};
{$R *.res} {$R *.res}
......
...@@ -216,6 +216,11 @@ ...@@ -216,6 +216,11 @@
<Form>fViewAddItem</Form> <Form>fViewAddItem</Form>
<DesignClass>TWebForm</DesignClass> <DesignClass>TWebForm</DesignClass>
</DCCReference> </DCCReference>
<DCCReference Include="View.QBInfo.pas">
<Form>FQBInfo</Form>
<FormType>dfm</FormType>
<DesignClass>TWebForm</DesignClass>
</DCCReference>
<None Include="index.html"/> <None Include="index.html"/>
<None Include="css\app.css"/> <None Include="css\app.css"/>
<None Include="config\config.json"/> <None Include="config\config.json"/>
......
...@@ -20,6 +20,13 @@ const ...@@ -20,6 +20,13 @@ const
API_MODEL = 'Api'; API_MODEL = 'Api';
type type
TQBInfo = class
public
clientID: string;
CompanyName: string;
CompanyID: string;
connected: boolean;
end;
TUserItem = class TUserItem = class
public public
...@@ -489,6 +496,7 @@ type ...@@ -489,6 +496,7 @@ type
[HttpGet] function GetRepUsers(): TList<TUserItem>; [HttpGet] function GetRepUsers(): TList<TUserItem>;
[HttpGet] function UpdateCustomer(QB_ID: string): TCustomerItem; [HttpGet] function UpdateCustomer(QB_ID: string): TCustomerItem;
[HttpGet] function UpdateItem(itemName: string): TItemItem; [HttpGet] function UpdateItem(itemName: string): TItemItem;
[HttpGet] function GetQBInfo(): TQBInfo;
function AddUser(userInfo: string): string; function AddUser(userInfo: string): string;
......
...@@ -41,6 +41,7 @@ type ...@@ -41,6 +41,7 @@ type
function GetRepUsers(): TList<TUserItem>; function GetRepUsers(): TList<TUserItem>;
function UpdateCustomer(QB_ID: string): TCustomerItem; function UpdateCustomer(QB_ID: string): TCustomerItem;
function UpdateItem(itemName: string): TItemItem; function UpdateItem(itemName: string): TItemItem;
function GetQBInfo(): TQBInfo;
function EditUser(const editOptions: string): string; function EditUser(const editOptions: string): string;
...@@ -87,6 +88,95 @@ implementation ...@@ -87,6 +88,95 @@ implementation
uses uses
XData.Sys.Exceptions, uLibrary, rOrderWeb, rOrderCutting; XData.Sys.Exceptions, uLibrary, rOrderWeb, rOrderCutting;
function TLookupService.GetQBInfo: TQBInfo;
var
restClient: TRESTClient;
restRequest: TRESTRequest;
restResponse: TRESTResponse;
param: TRESTRequestParameter;
res: string;
jsValue: TJSONValue;
jsObj: TJSONObject;
CompanyInfoList: TJSONArray;
CompanyInfo: TJSONObject;
I: integer;
AccessToken, RefreshToken, CompanyID, Client, Secret, SQL, desc, BaseUrl: string;
LastRefresh: TDateTime;
iniFile: TIniFile;
begin
logger.Log(3, 'TLookupService.GetQBItems');
iniFile := nil;
restClient := nil;
restRequest := nil;
restResponse := nil;
try
try
result := TQBInfo.Create;
iniFile := TIniFile.Create(ExtractFilePath(Application.ExeName) + 'kgOrdersServer.ini');
restClient := TRESTClient.Create(nil);
restRequest := TRESTRequest.Create(nil);
restResponse := TRESTResponse.Create(nil);
result.clientID := iniFile.ReadString('Quickbooks', 'ClientID', '');
if iniFile.ReadString('Quickbooks', 'CompanyID', '') = '' then
begin
result.connected := false;
end
else
begin
restRequest.Client := restClient;
restRequest.Response := restResponse;
if iniFile.ReadString('Quickbooks', 'LastRefresh', '') = '' then
LastRefresh := 0
else
LastRefresh := StrToDateTime(iniFile.ReadString('Quickbooks', 'LastRefresh', ''));
if MinutesBetween(Now, LastRefresh) > 58 then
RefreshAccessToken();
Client := iniFile.ReadString('Quickbooks', 'ClientID', '');
Secret := iniFile.ReadString('Quickbooks', 'ClientSecret', '');
CompanyID := iniFile.ReadString('Quickbooks', 'CompanyID', '');
RefreshToken := iniFile.ReadString('Quickbooks', 'RefreshToken', '');
AccessToken := iniFile.ReadString('Quickbooks', 'AccessToken', '');
BaseUrl := iniFile.ReadString('Quickbooks', 'BaseUrl', '');
restClient.BaseURL := BaseUrl;
restRequest.Method := rmGET;
res := '/v3/company/' + companyID + '/query?query=select * from CompanyInfo&minorversion=75';
restRequest.Resource := res;
param := restRequest.Params.AddItem;
param.Name := 'Authorization';
param.Kind := pkHTTPHEADER;
param.Options := param.Options + [TRESTRequestParameterOption.poDoNotEncode];
param.Value := 'Bearer ' + AccessToken;
restRequest.Execute;
jsValue := restResponse.JSONValue;
jsObj := TJSONObject(jsValue);
CompanyInfoList := TJSONArray(TJSONObject(jsObj.GetValue('QueryResponse')).GetValue('CompanyInfo'));
CompanyInfo := CompanyInfoList.Items[I] as TJSONObject;
result.CompanyName := CompanyInfo.GetValue('CompanyName').Value;
result.CompanyID := CompanyID;
result.connected := true;
end;
except
on E: Exception do
begin
Logger.Log(1, 'Error in getQBItems: ' + E.Message);
raise EXDataHttpException.Create(500, 'Unable to retrieve QuickBooks Items: A QuickBooks interface error has occurred!');
end;
end;
finally
iniFile.Free;
restClient.Free;
restRequest.Free;
restResponse.Free;
end;
end;
function TLookupService.addEstimate(orderInfo: string): TJSONObject; function TLookupService.addEstimate(orderInfo: string): TJSONObject;
var var
......
...@@ -114,11 +114,11 @@ ...@@ -114,11 +114,11 @@
<VerInfo_Locale>1033</VerInfo_Locale> <VerInfo_Locale>1033</VerInfo_Locale>
<DCC_ExeOutput>.\bin</DCC_ExeOutput> <DCC_ExeOutput>.\bin</DCC_ExeOutput>
<DCC_UnitSearchPath>C:\RADTOOLS\FastMM4;$(DCC_UnitSearchPath)</DCC_UnitSearchPath> <DCC_UnitSearchPath>C:\RADTOOLS\FastMM4;$(DCC_UnitSearchPath)</DCC_UnitSearchPath>
<VerInfo_Keys>CompanyName=EM Systems;FileDescription=$(MSBuildProjectName);FileVersion=0.9.13.7;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=0.9.11;Comments=</VerInfo_Keys> <VerInfo_Keys>CompanyName=EM Systems;FileDescription=$(MSBuildProjectName);FileVersion=0.9.13.8;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=0.9.11;Comments=</VerInfo_Keys>
<VerInfo_MajorVer>0</VerInfo_MajorVer> <VerInfo_MajorVer>0</VerInfo_MajorVer>
<VerInfo_MinorVer>9</VerInfo_MinorVer> <VerInfo_MinorVer>9</VerInfo_MinorVer>
<VerInfo_Release>13</VerInfo_Release> <VerInfo_Release>13</VerInfo_Release>
<VerInfo_Build>7</VerInfo_Build> <VerInfo_Build>8</VerInfo_Build>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Cfg_1_Win64)'!=''"> <PropertyGroup Condition="'$(Cfg_1_Win64)'!=''">
<AppDPIAwarenessMode>PerMonitorV2</AppDPIAwarenessMode> <AppDPIAwarenessMode>PerMonitorV2</AppDPIAwarenessMode>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment