One to Many Form
Moderator: Rathinagiri
Re: One to Many Form
Sudip good-going!
I believe two kind of entry modules are mainly required for business applications. One is Master Entry form and another is Transaction Entry (Sales, Purchase, Voucher etc.). A good transaction entry module with detail part (the kind of Sudip has demonstrated here) helps us to learn many things.
Sudip, your this transaction entry needs many enhancements and fixes before it qualifies for a transaction entry module at a professional level.
My 2 cents (based on the modified code uploaded by Giri):
1) When I press Enter in the Amount column, the cursor/focus should move to the next row and 1st column. I believe it takes time to have a control over a grid entry. Giri has introduced "Insert" option but that can be an optional, user will not press/click "insert" everytime.
2)You are allowing entry in the Amount column (which is valid), but if u change the amount, your calculation function changes it again (qty*rate). Though this is not HMG issue.
3)In this grid entry - the user should have all & same flexibility during entry & edit mode.
4)ONE NEW IDEA:
---------------
In HMG grid, can wallpaper like feature be inserted as background picture. If yes this a grid can look attractive and more presentable in certain cases.
5)U've used combo box in grid - well do u remember voucher entry interface of Tally. How about this:
start typing in the product column and a help list will appear - quick search ur product and press enter. The value gets returned to cell. Combo box is not good when there is huge items - we can't quick search...only the 1st char search works, I suppose. Yes if ur combo box comes with textbox where one write and the cursor of the combobox moves as per the text, then can be considered.
6)Master Help list (customer/product etc.):
ur filling them in array. Right? But will it b not time-consuming when real-life data is there. And if u fill the arrays initially say at the start of the module or application then what about the real time data if the application is running in a network. We should try to fetch data/datasource directly from the dbf/database and pass it to the control which gets invoked when Help list is called.
7)How to make new invoice entry in ur this module?!
With regards,
Swapan
I believe two kind of entry modules are mainly required for business applications. One is Master Entry form and another is Transaction Entry (Sales, Purchase, Voucher etc.). A good transaction entry module with detail part (the kind of Sudip has demonstrated here) helps us to learn many things.
Sudip, your this transaction entry needs many enhancements and fixes before it qualifies for a transaction entry module at a professional level.
My 2 cents (based on the modified code uploaded by Giri):
1) When I press Enter in the Amount column, the cursor/focus should move to the next row and 1st column. I believe it takes time to have a control over a grid entry. Giri has introduced "Insert" option but that can be an optional, user will not press/click "insert" everytime.
2)You are allowing entry in the Amount column (which is valid), but if u change the amount, your calculation function changes it again (qty*rate). Though this is not HMG issue.
3)In this grid entry - the user should have all & same flexibility during entry & edit mode.
4)ONE NEW IDEA:
---------------
In HMG grid, can wallpaper like feature be inserted as background picture. If yes this a grid can look attractive and more presentable in certain cases.
5)U've used combo box in grid - well do u remember voucher entry interface of Tally. How about this:
start typing in the product column and a help list will appear - quick search ur product and press enter. The value gets returned to cell. Combo box is not good when there is huge items - we can't quick search...only the 1st char search works, I suppose. Yes if ur combo box comes with textbox where one write and the cursor of the combobox moves as per the text, then can be considered.
6)Master Help list (customer/product etc.):
ur filling them in array. Right? But will it b not time-consuming when real-life data is there. And if u fill the arrays initially say at the start of the module or application then what about the real time data if the application is running in a network. We should try to fetch data/datasource directly from the dbf/database and pass it to the control which gets invoked when Help list is called.
7)How to make new invoice entry in ur this module?!
With regards,
Swapan
- Rathinagiri
- Posts: 5477
- Joined: Tue Jul 29, 2008 6:30 pm
- DBs Used: MariaDB, SQLite, SQLCipher and MySQL
- Location: Sivakasi, India
- Contact:
Re: One to Many Form
Hi Swapan,
Regarding Combobox, the combobox items can be searched by pressing repeated key strokes if you had sorted it out already.
Regarding Grid background image, it can't be possible as of now, but we can have colors. (even for alternative rows/cols)
Regarding array handling too, while processing, it shall be in the main memory and every time we take network and database resource would be a problem too. However, I am afraid an invoice can't have more than 100 items. Isn't it? Anyway, it is programmers' problem.
I had introduced a new option here in Sudip's code. You can verify that too.
Except background image, I think we can achieve most of your suggestions. Thanks.
Regarding Combobox, the combobox items can be searched by pressing repeated key strokes if you had sorted it out already.
Regarding Grid background image, it can't be possible as of now, but we can have colors. (even for alternative rows/cols)
Regarding array handling too, while processing, it shall be in the main memory and every time we take network and database resource would be a problem too. However, I am afraid an invoice can't have more than 100 items. Isn't it? Anyway, it is programmers' problem.
I had introduced a new option here in Sudip's code. You can verify that too.
Except background image, I think we can achieve most of your suggestions. Thanks.
East or West HMG is the Best.
South or North HMG is worth.
...the possibilities are endless.
South or North HMG is worth.
...the possibilities are endless.
Re: One to Many Form
Dear Swapan,
Thank you very much for all your advice and I must say that all of them are very PRACTICAL specially in the environment we are working.
I am learning HMG. If you tell me to do all those things in VFP, I have no problem. But, I don't know the language thoroughly and please remember Designer/Creator of HMG designed it in a very Simple way, where you may feel something missing at first sight. After some times when you get clarification from persons who are more experienced, everything will be clear.
I also have many doubts (may be more than you ), but I always want to seek the solution in my off-time. Sometimes it becomes clear, sometimes it takes more time to be clear.
But, I am sure, I can solve all your advice within a month with HMG
Please try SET NEVIGATION EXTENDED in your software (regarding Tab/Enter problem)
(Now I have to write next part of "OUR" one-to-many form, i.e., Add, Save, Delete)
With best regards.
Sudip
Thank you very much for all your advice and I must say that all of them are very PRACTICAL specially in the environment we are working.
I am learning HMG. If you tell me to do all those things in VFP, I have no problem. But, I don't know the language thoroughly and please remember Designer/Creator of HMG designed it in a very Simple way, where you may feel something missing at first sight. After some times when you get clarification from persons who are more experienced, everything will be clear.
I also have many doubts (may be more than you ), but I always want to seek the solution in my off-time. Sometimes it becomes clear, sometimes it takes more time to be clear.
But, I am sure, I can solve all your advice within a month with HMG
Please try SET NEVIGATION EXTENDED in your software (regarding Tab/Enter problem)
(Now I have to write next part of "OUR" one-to-many form, i.e., Add, Save, Delete)
With best regards.
Sudip
With best regards,
Sudip
Sudip
Re: One to Many Form
Thank you Rathinagiri,
Thanks for the help. As, I know the language relatively less, I can't answer Swapan's questions ;(
Regards.
Sudip
Thanks for the help. As, I know the language relatively less, I can't answer Swapan's questions ;(
Regards.
Sudip
With best regards,
Sudip
Sudip
Re: One to Many Form Ver 1.0
Hi,
Thank you all for your hearty support in my learning HMG
I added some attributes and functions in "OUR" One2Many Form Demo Project. I sincerely believe in using the word "OUR", because it's not only me who is learning HMG through the project, "WE", the learners of HMG are learning through this project.
Add, Delete, Save, Cancel functions are added. One check is added to check any change is made or not.
Please advise as before to improve the project.
One thing more. As it becomes bigger, next time I shall upload zip file (Is zip allowd?, .prg is not allowed. I tried it)
Thank you Rathinagiri for helping so much. Hi Alex and Swapan, thank you all. And thank you CCH, you always kept my focus on HMG (Sorry, I couldn't answer your mail due to the project)
With best regards.
Sudip
Thank you all for your hearty support in my learning HMG
I added some attributes and functions in "OUR" One2Many Form Demo Project. I sincerely believe in using the word "OUR", because it's not only me who is learning HMG through the project, "WE", the learners of HMG are learning through this project.
Add, Delete, Save, Cancel functions are added. One check is added to check any change is made or not.
Please advise as before to improve the project.
One thing more. As it becomes bigger, next time I shall upload zip file (Is zip allowd?, .prg is not allowed. I tried it)
Thank you Rathinagiri for helping so much. Hi Alex and Swapan, thank you all. And thank you CCH, you always kept my focus on HMG (Sorry, I couldn't answer your mail due to the project)
With best regards.
Sudip
Code: Select all
#include "dbstruct.ch"
#include "minigui.ch"
static aGrid := {}, aCustSource := {}, aCustDisp := {}, ;
aItemSource := {}, aItemDisp := {}, aItemRate := {}, ;
lChanged := .f., lAddMode := .f.
Function Main
local i, cb
SetProg()
CreateTable()
select sale
set order to tag invno
go top
CreateLookup()
DEFINE WINDOW Form_1 ;
AT 0,0 ;
WIDTH 640 ;
HEIGHT 430 ;
TITLE "One To Many Form (Ver 1.0)" ;
MAIN ;
on init RefreshWin()
@ 10, 10 label lblInvno value "Invoice No.:"
@ 10, 100 textbox txtInvno ;
readonly
@ 10, 300 label lblInvdt value "Invoice Dt.:"
@ 10, 400 datepicker dpkInvdt ;
on change ValChanged()
@ 40, 10 label lblCustcd value "Customer:"
@ 40, 100 COMBOBOX cboCustcd ;
items aCustDisp ;
width 200 ;
on change ValChanged()
@ 70,10 GRID grdSaledtl ;
WIDTH 550 ;
HEIGHT 250 ;
HEADERS {'Item', 'Rate', 'Qty', 'Amt'} ;
WIDTHS {200,100,100,100} ;
COLUMNCONTROLS { {'COMBOBOX', aItemDisp}, ;
{'TEXTBOX', 'NUMERIC', '999999.99'}, ;
{'TEXTBOX', 'NUMERIC', '999999'}, ;
{'TEXTBOX', 'NUMERIC', '99999999.99'}} ;
edit ;
justify {0,1,1,1};
on lostfocus calcamt();
on change ValChanged()
@ 320, 10 label lblMesg1 value "(Ins: Add New, Del: Delete)" width 200
@ 330, 300 label lblTotamt value "Total:"
@ 330, 400 textbox txtTotamt ;
readonly ;
numeric ;
inputmask "9999999.99"
@ 360, 10 button cmdPrev caption "&Previous" ;
action MovePrev()
@ 360, 110 button cmdNext caption "&Next" ;
action MoveNext()
@ 360, 210 button cmdAdd caption "&Add" ;
action AddRec()
@ 360, 310 button cmdDelete caption "&Delete" ;
action DeleteRec()
@ 360, 410 button cmdSave caption "&Save" ;
action SaveRec()
@ 360, 510 button cmdCancel caption "&Cancel" ;
action CancelRec()
END WINDOW
on key DELETE of form_1 action dodel()
on key INSERT of form_1 action doins()
CENTER WINDOW Form_1
ACTIVATE WINDOW Form_1
Return
FUNCTION CreateLookup()
local i
aItemSource := {}
aItemDisp := {}
aItemRate := {}
aCustSource := {}
aCustDisp := {}
select item
set order to tag itemnm
do while !eof()
aadd(aItemSource, item->itemcd)
aadd(aItemDisp, item->itemnm)
aadd(aItemRate, item->rate)
skip
enddo
select cust
set order to tag custnm
do while !eof()
aadd(aCustSource, cust->custcd)
aadd(aCustDisp, cust->custnm)
skip
enddo
return nil
FUNCTION RefreshWin()
if lAddMode
select sale
go bottom
form_1.txtInvno.value := str(val(sale->invno)+1, 10)
form_1.dpkInvdt.value := date()
form_1.cboCustcd.value := 0
form_1.txtTotamt.value := 0.00
aGrid := {}
form_1.grdSaledtl.DeleteAllItems()
else
form_1.txtInvno.value := sale->invno
form_1.dpkInvdt.value := sale->invdt
form_1.cboCustcd.value := ascan(aCustSource, {|x| x == sale->custcd})
form_1.txtTotamt.value := sale->totamt
aGrid := {}
select saledtl
set order to tag invno
locate for invno = sale->invno
do while !eof()
aadd(aGrid, {ascan(aItemSource, {|x| x == saledtl->itemcd}), ;
saledtl->rate, saledtl->qty, saledtl->rate*saledtl->qty})
continue
enddo
form_1.grdSaledtl.DeleteAllItems()
for i = 1 to len(aGrid)
form_1.grdSaledtl.additem(aGrid[i])
next
endif
return nil
PROCEDURE CalcAmt()
local i, nIctr, aTemp := {}, mTotAmt := 0.00
// lChange := .t.
i := form_1.grdSaledtl.value
nIctr := form_1.grdSaledtl.cell(i, 1)
aTemp := form_1.grdSaledtl.item(i)
if i < 1
return
endif
aTemp[2] := iif(nIctr>0 .and. nIctr<=len(aItemRate), aItemRate[nIctr], 0.00)
aTemp[4] := aTemp[2]*aTemp[3]
form_1.grdSaledtl.item(i) := aTemp
for i = 1 to form_1.grdSaledtl.Itemcount
mTotAmt += form_1.grdSaledtl.cell(i, 4)
next
form_1.txtTotamt.value := mTotAmt
return
PROCEDURE MovePrev()
if IsChanged()
return
endif
select sale
if !bof()
skip -1
endif
if bof()
go top
endif
RefreshWin()
PROCEDURE MoveNext()
if IsChanged()
return
endif
select sale
if !eof()
skip
endif
if eof()
go bottom
endif
RefreshWin()
FUNCTION ValChanged()
lChanged := .t.
return nil
FUNCTION IsChanged()
if lAddMode .or. lChanged
msgbox("Record Changed. Please Save or Cancel."); return .t.
endif
return .f.
FUNCTION AddRec()
if IsChanged()
return
endif
lAddMode := .t.
lChanged := .t.
RefreshWin()
return nil
FUNCTION DeleteRec()
if IsChanged()
return
endif
if !MsgYesNo("Do you really want to delete?")
return
endif
select sale
if eof()
msgbox("Cannot Delete"); return
endif
select saledtl
delete all for invno = sale->invno
select sale
delete
skip
if eof()
go bottom
endif
RefreshWin()
FUNCTION SaveRec()
local i
select sale
if eof()
msgbox("Please Add and then Save...");return
endif
if empty(form_1.dpkInvdt.value)
msgbox("Please specify Invoice date!"); form_1.dpkInvdt.setfocus(); return
endif
if empty(form_1.cboCustcd.value)
msgbox("Please specify Customer!"); form_1.cboCustcd.setfocus(); return
endif
if empty(form_1.grdSaledtl.itemcount)
msgbox("Invoice must have Items!"); form_1.grdSaledtl.setfocus();return
endif
for i := 1 to form_1.grdSaledtl.itemcount
do case
case empty(form_1.grdSaledtl.cell(i, 1))
msgbox("Please specify Item!");return
case empty(form_1.grdSaledtl.cell(i, 2))
msgbox("Blank Rate not allowed!");return
case empty(form_1.grdSaledtl.cell(i, 3))
msgbox("Blank Qty not allowed!");return
endcase
next
select sale
if lAddMode
append blank
endif
sale->invno := form_1.txtInvno.value
sale->invdt := form_1.dpkInvdt.value
sale->custcd := aCustSource[form_1.cboCustcd.value]
sale->totamt := form_1.txtTotamt.value
select saledtl
if !lAddmode
delete all for invno = sale->invno
endif
for i := 1 to form_1.grdSaledtl.itemcount
append blank
saledtl->invno := sale->invno
saledtl->itemcd := aItemSource[form_1.grdSaledtl.cell(i, 1)]
saledtl->rate := form_1.grdSaledtl.cell(i, 2)
saledtl->qty := form_1.grdSaledtl.cell(i, 3)
next
lAddMode := lChanged := .f.
RefreshWin()
return nil
FUNCTION CancelRec()
lAddMode := lChanged := .f.
RefreshWin()
return nil
Function SetProg()
set talk off
set dele on
set date brit
set cent on
set epoch to 1950
SET BROWSESYNC ON
SET NAVIGATION EXTENDED
SET INTERACTIVECLOSE QUERY MAIN
set font to "Arial", 09
REQUEST DBFCDX , DBFFPT
RDDSETDEFAULT( "DBFCDX" )
return nil
FUNCTION CreateTable()
local aDbf := {}
if !file("item.dbf")
aDbf := {}
aadd(adbf, {"itemcd", "c", 10, 0})
aadd(adbf, {"itemnm", "c", 20, 0})
aadd(adbf, {"rate", "n", 8, 2})
dbcreate("item", adbf)
use item
append blank
replace itemcd with "COMP", itemnm with "Computer", rate with 20000.00
append blank
replace itemcd with "CD", itemnm with "Compact Disk", rate with 10.00
append blank
replace itemcd with "OPM", itemnm with "Optical Mouse", rate with 400.00
use
endif
if !file("cust.dbf")
aDbf := {}
aadd(adbf, {"custcd", "c", 10, 0})
aadd(adbf, {"custnm", "c", 20, 0})
dbcreate("cust", adbf)
use cust
append blank
REPLACE CUSTCD WITH "CUST1", CUSTNM WITH "Customer One"
append blank
REPLACE CUSTCD WITH "CUST2", CUSTNM WITH "Customer Two"
append blank
REPLACE CUSTCD WITH "CUST3", CUSTNM WITH "Customer Three"
use
endif
if !file("sale.dbf")
aDbf := {}
aadd(adbf, {"invno", "c", 10, 0})
aadd(adbf, {"invdt", "d", 8, 0})
aadd(adbf, {"custcd", "c", 10, 0})
aadd(adbf, {"totamt", "n", 10, 2})
dbcreate("sale", adbf)
use sale
append blank
replace invno with str(1, 10), invdt with date(), custcd with "CUST1", totamt with 40200.00
append blank
replace invno with str(2, 10), invdt with date(), custcd with "CUST2", totamt with 420
use
endif
if !file("saledtl.dbf")
aDbf := {}
aadd(adbf, {"invno", "c", 10, 0})
aadd(adbf, {"itemcd", "c", 10, 0})
aadd(adbf, {"rate", "n", 8, 2})
aadd(adbf, {"qty", "n", 6, 0})
dbcreate("saledtl", adbf)
use saledtl
append blank
replace invno with str(1, 10), itemcd with "COMP", rate with 20000.00, qty with 2
append blank
replace invno with str(1, 10), itemcd with "CD", rate with 10.00, qty with 20
append blank
replace invno with str(2, 10), itemcd with "CD", rate with 10.00, qty with 5
append blank
replace invno with str(2, 10), itemcd with "OPM", rate with 400.00, qty with 1
use
endif
close databases
use item new
use cust new
use sale new
use saledtl new
select item
index on itemcd tag itemcd
index on upper(itemnm) tag itemnm
select cust
index on custcd tag custcd
index on upper(custnm) tag custnm
select sale
index on invno tag invno
index on invdt tag invdt
index on custcd tag custcd
select saledtl
index on invno tag invno
index on itemcd tag intemcd
return nil
function dodel
local lineno := form_1.grdsaledtl.value
if lineno > 0
form_1.grdsaledtl.deleteitem(lineno)
if lineno > 1
form_1.grdsaledtl.value := lineno - 1
else
if form_1.grdsaledtl.itemcount > 0
form_1.grdsaledtl.value := 1
endif
endif
endif
calcamt()
return nil
function doins
//if form_1.grdSaledtl.cell(form_1.grdsaledtl.itemcount, 4) > 0.0
// form_1.grdsaledtl.additem({1,aitemrate[1],0,0.00})
//endif
form_1.grdsaledtl.additem({0,0.00,0,0.00})
form_1.grdSaledtl.value := form_1.grdsaledtl.itemcount
return nil
With best regards,
Sudip
Sudip
Re: One to Many Form
Hi,
Please incorporate following code in Grid definition:- (Please check columnwhen)
Using this, user can change Item and Qty fields only, not Rate and Amt fields.
Tank you Rathi, I did it !
Regards.
Sudip
Please incorporate following code in Grid definition:- (Please check columnwhen)
Using this, user can change Item and Qty fields only, not Rate and Amt fields.
Code: Select all
@ 70,10 GRID grdSaledtl ;
WIDTH 550 ;
HEIGHT 250 ;
HEADERS {'Item', 'Rate', 'Qty', 'Amt'} ;
WIDTHS {200,100,100,100} ;
COLUMNCONTROLS { {'COMBOBOX', aItemDisp}, ;
{'TEXTBOX', 'NUMERIC', '999999.99'}, ;
{'TEXTBOX', 'NUMERIC', '999999'}, ;
{'TEXTBOX', 'NUMERIC', '99999999.99'}} ;
edit ;
justify {0,1,1,1};
on lostfocus calcamt();
on change ValChanged();
COLUMNWHEN { {|| .t.},{|| .f.}, {|| .t.}, {|| .f.}}
Regards.
Sudip
With best regards,
Sudip
Sudip
Re: One to Many Form
Thanks Giri & Sudip for going through my post and taking my feedback in good spirit. Acutally Sudip I'm also considering your this project as my learning example. Yes, its "ours" project and we all can reap benefits from it provided we work on it.
2day, hope to go through this project more seriously..............
BTW: One more feature grid feature came 2 my mind!
Do/Can we have HIDDEN COLUMNS in our HMG grid?
Regards,
Swapan
2day, hope to go through this project more seriously..............
BTW: One more feature grid feature came 2 my mind!
Do/Can we have HIDDEN COLUMNS in our HMG grid?
Regards,
Swapan
- Rathinagiri
- Posts: 5477
- Joined: Tue Jul 29, 2008 6:30 pm
- DBs Used: MariaDB, SQLite, SQLCipher and MySQL
- Location: Sivakasi, India
- Contact:
Re: One to Many Form
Yes. By defining the width of the column as 0.Do/Can we have HIDDEN COLUMNS in our HMG grid?
East or West HMG is the Best.
South or North HMG is worth.
...the possibilities are endless.
South or North HMG is worth.
...the possibilities are endless.
- Rathinagiri
- Posts: 5477
- Joined: Tue Jul 29, 2008 6:30 pm
- DBs Used: MariaDB, SQLite, SQLCipher and MySQL
- Location: Sivakasi, India
- Contact:
Re: One to Many Form
Yes. You did it, even without any modification in the source code. I didn't know this feature. Thanks for sharing.Thank you Rathi, I did it !
And, you had not provided code for valchanged() function.
East or West HMG is the Best.
South or North HMG is worth.
...the possibilities are endless.
South or North HMG is worth.
...the possibilities are endless.
Re: One to Many Form
Dear Rathi,
I already gave ValChanged() function in my previous post. But due to bad indentation (I use latest version of xEditw from xHarbour.com and Indent program came as utility from MiniGui Ext) it looks hidden.
Please look next to "PROCEDURE MoveNext()" of my given code. If by any chance you don't find it, I am giving the following code.
BTW, can anyone suggest me a good source code editor which has color mark for Harbour/xHarbour syntax?
Thank you all for helping me to learn HMG
With best regards.
Sudip
I already gave ValChanged() function in my previous post. But due to bad indentation (I use latest version of xEditw from xHarbour.com and Indent program came as utility from MiniGui Ext) it looks hidden.
Please look next to "PROCEDURE MoveNext()" of my given code. If by any chance you don't find it, I am giving the following code.
Code: Select all
FUNCTION ValChanged()
lChanged := .t.
return nil
Thank you all for helping me to learn HMG
With best regards.
Sudip
With best regards,
Sudip
Sudip