# Native Code > برنامه نویسی در Delphi > ابزارهای گزارش سازی در دلفی >  مانده حساب بدهکار بستانکار در Fast Report

## فرزاد دلفی باز

با سلام :
من مشکلی مواجه ام که طبق شکل زیر می خواهم مثلا در سطر اول 7,000,000 رو با 0 جمع کنه و در مانده همان سطر مبلغ 7,000,000 بیاید در سطر دوم باید 7,000,000 مانده سظر اول رابا 3,000,000جمع و 10,000,000 را در مانده سطر دوم بیآورد و برای آخری 10,000,000  را از 2,000,000 کم کند و نتیجه را بدهد...
 آیا در فست چنین کاری شدنی است 



با تشکر

----------


## Will_Smith

عزیز دل چند بار بحث شده
اطلاعات بیشتر از مواردی که بحث شده در سایت گیرت نمیاد
گشتم نبود نگرد نیست

----------


## فرزاد دلفی باز

دوست عزیز میشه آدرس جایی رو که این بحث به نتیجه رسیده رو به من بدی .... 




> گشتم نبود نگرد نیست


با تشکر

----------


## Will_Smith

https://barnamenevis.org/showth...86%D8%AF%D9%87
https://barnamenevis.org/showth...86%D8%AF%D9%87

----------


## فرزاد دلفی باز

دوست عزیز من می خواهم این مشکل در fast report حل بشه که در این قسمت مطرح کردم نه در  sql server (در هر صورت ممنون لطف کردی).....

----------


## فرزاد دلفی باز

آقا کسی نمی تونه ما رو کمک کنه !!!!!!!!!

----------


## Mohammad_Mnt

یه متغیر توی فست ریپورت درست کن و مقداری را که می خوای براش تعریف کن. بعد از یه کامپوننت Memo استفاده کن تا مقدار متغیر را نشون بدی

----------


## kamal_habibi

> با سلام :
> من مشکلی مواجه ام که طبق شکل زیر می خواهم مثلا در سطر اول 7,000,000 رو با 0 جمع کنه و در مانده همان سطر مبلغ 7,000,000 بیاید در سطر دوم باید 7,000,000 مانده سظر اول رابا 3,000,000جمع و 10,000,000 را در مانده سطر دوم بیآورد و برای آخری 10,000,000 را از 2,000,000 کم کند و نتیجه را بدهد...
> آیا در فست چنین کاری شدنی است 
> 
> 
> 
> با تشکر


 
با سلام خدمت آقای دلفی باز این قدر سطر و ستون کردید که من درس متوجه منظور شما نشدم 
فکر کنم شما میخواهید اعداد موجود در یک سطر را در Fast Report با هم جمع کنید و در آخر
مجموع ستون اول را از مجموع ستون بعدی کم کنید ؟ درسته ؟

----------


## champion

فرزاد خان شاید ژاسخ من یک کم دیر باشه ولی خب از هیچی بهتره شما باید توی Fastrep یک متغیر تعریف کنی و توی Beforeprint باند Detail  مقدار اون رو با Bed - bes جمع کنی یعنی بنویسی
My_var = My_var + (bed - bes)  و بعد این متغیر رو مقدارشو مثلا توی یک ممو و یا یک متغیر دیگه که از قبل تعریف کدی و باهاش مثل فیلد رفتار شده بریزی البته یادت نره که متغیر My_Var‌  رو توی beforePrint باند Master مساوی صفر قرار بدی
اگه متوجه نشدی بگو برات کدشو بنویسم

----------


## فرزاد دلفی باز

آقا ممنون لطف کردید..............

----------


## Amir_Safideh

با سلام به دوستای گلم.
من امروز یه روش پیدا کردم که میشه ستون مانده حساب رو در زمان گزارشگیری ساخت یعنی نیازی نیست که فرمولی برای محاسبه فیلد مانده حساب توی کوئری نوشت یکی از محاسن این روش سرعت خوبشه اما به خاطر اینکه فکر میکنم که الان خیلی دیره برای جواب دادن و به خاطر اینکه یه کم سرم شلوغه فعلا چیزی نمیگم تا ببینم کسی از دوستان دنبال این جواب هست یا نه اگر کسی بود من مخلصش هم هستم در اسرع وقت جواب سوال رو میدم .

----------


## szabeh

بله بیزحمت لطف کنید.

----------


## Amir_Safideh

دوست عزیز سلام
من امروز فهمیدم که این روش جدید من یه مشکلی داره الان میگم مشکل چیه . من قبلا برای محاسبه فیلد مانده حساب از روش ایجاد یک Temp Table استفاده میکردم که احتمالا میدونید چیه این روش خیلی خوب و دقیقیه ولی عیبش سرعت پائین گزارشگیری است در زمانی که حجم داده ها بالاست مثلا برای گزارش یه حساب با 2000 رکورد حدود 18 ثانیه زمان لازم داره به همین دلیل من به فکر روش دیگه ای افتادم و اون روش هم اینه : 
من توی کوئری ای که نوشتم برای گزارش گیری دو فیلد بدهکار و بستانکار رو گزاشتم بعلاوه یه سری فیلدهای معمول مثل شرح و غیره ... و در قسمت Field Editor مربوط به DataSet یه Right Click - New Field و یک فیلد از نوع Calculated ساختم و اسم این فیلد رو گذاشتم Remain و بعد در قسمت Eventes در رویداد OnCalcFields مربوط به این DataSet این کد رو نوشتم :

ابتدا یک متغییر به نام Remain که مقدار مانده رکورد قبل رو نگه میداره 

  public
    { Public declarations }
  end;
var
  DM: TDM;
  Remain: Real;
 



و سپس این کد :


procedure TDM.SPS_Bill_ReportCalcFields(DataSet: TDataSet);
begin
  if DataSet.FieldByName('Bed').AsFloat > 0 then
    Remain := Remain + DataSet.FieldByName('Bed').AsFloat
  else
    Remain := Remain - DataSet.FieldByName('Bes').AsFloat;
  DataSet.FieldByName('Remain').AsFloat := Remain;
end;


و در رویداد After Close مقدار متغییر رو برابر با صفر قرار دادم تا بعد از هر بار اجرا شدن مجدد کوئری محاسبات از صفر شروع بشه 

procedure TDM.SPS_Bill_ReportAfterClose(DataSet: TDataSet);
begin
  Remain := 0;
end;


زمانی که کوئری اجرا میشه همه چیز درسته ولی از اونجا که رویداد OnCalcFields در Navigate کردن DataSet نیز اجرا میشه و در ضمن از اونجا که این رویداد در اصل برای انجام محاسبه بر روی رکورد جاریست به همین دلیل بعد Navigate همه چیز به هم میریزه . حالا من دارم دنبال راه حل این مشکل میگردم .. 
شما هم امتحان کنید اگر راه حلی پیدا کردید من رو در جریان بزارید لطفا .

----------


## JAFO_IRAN

سلام

احتمالا درست متوجه نشدم مشکل چیه و یا راه حل ساده زیر مشکلی داره که الان درست متوجه نمیشم:

اگه در هر detail band یک "جمع" داشته باشیم (با استفاده از امکانات report generator و شبیه همون جمع هایی که در انتهای گزارش میان - ولی در هر سطر) اونوقت مانده مورد نظر خود به خود حاصل میشه. اگه report generator یک کم امکان فرمول نویسی داشته باشه کافیه - مثلا در quick report میشد گفت 

Sum(tr_Deb - tr_Cre)


که فیلدهای بدهکار و بستانکار با هم لحاظ شده باشند.

ارادت

----------


## Amir_Safideh

با سلام .
مشکل در اصل اینه که چیزی که ما اینجا لازم داریم اینه که این عملیات محاسبه بشه :

(مانده در رکورد قبلی + بدهکار در رکورد جاری - بستانکار در رکورد جاری) = مانده رکورد جاری

ممنون از توجهتون .

----------


## JAFO_IRAN

سلام و ممنون از پاسخ

1. هر زمان بحث مانده در رکورد "قبلی" مطرح است یعنی "ترتیب" گزارش دارای اهمیت است.
2. برای اینکه گزارش منطقی باشد لازم است تا "دوره" اخذ مانده تعریف شده باشد. در سیستم‌های مالی، این دوره معمولا همان سال مالی است
3. برای اینکه در دفعات مختلف اخذ گزارش پاسخ مشابهی بدست آید باید ترتیبی دهیم که محاسبه مانده ارتباطی با محدوده رکوردهایی که چاپ میشوند نداشته باشد
4. برای اینکه مانده سطر نخست گزارش دارای معنی و قابل توجیه باشد لازم است تا جمع عملیات تا پیش از آغاز محدوده اخذ گزارش نیز در گزارش منعکس شود. این جمع برای ستونهای بدهکار و بستانکار در گزارش بازنمایی خواهد شد.
5. با وجود این سطر جمع، درست مانند این خواهد بود که یک رکورد دارای هر دو مقدار بدهکار و بستانکار داشته باشیم که اختلاف آنها مانده ناشی از رکوردهای پیش از آغاز محدوده را بدست میدهد.
6. برای بدست دادن جمع عملیات پیش از آغاز محدوده مثلا میتوانیم از union استفاده کنیم.
7. در نتیجه با همان المان جمع حاوی اختلاف مبالغ بدهکار و بستانکار نتیجه دلخواه حاصل میشود:
الف - اگر گزارش از ابتدای دوره مالی اخذ شود در سطراول مانده رکورد اول و در سطر جمع مانده "ناشی از رکورد دوم" و مانده رکورد اول و به همین ترتیب تا آخر را خواهیم داشت
ب - اگر گزارش از ابتدا نباشد، مانده تا ابتدای گزارش در سطر اول بازنمایی میشود و همان المان جمع مانده مناسب را در سایر سطرها بدست خواهد داد.

8. معمولا المانهای "جمع" در سیستم های اخذ گزارش دارای یک تنظیم مهم هستند با مفهومی چون "حاصل، پس از چاپ صفر شود". لازم است تا در این کاربرد، حتما ترتیبی دهیم که حاصل مزبور صفر نشود.


ارادت

----------


## Amir_Safideh

با سلام 
دوست عزیز این با توجه به چیزی که میفرمائید این کدی که لطف کردید (منظورم این کده)

Sum(tr_Deb - tr_Cre)

کاملا درسته ولی مشکل اصلی اینه که این کد دقیقا اون چیزی رو که میخوای برنمیگردونه 
به طور مثال صرف نظر از دوره مالی و صرف نظر از اینکه ما اطلاعات رو فیلتر کنیم تصور کنید که ما یه حسابی داریم که دارای صدتا رکورده . حالا ما این کدی رو که شما لطف کردید رو برای فیلد مانده حساب استفاده میکنیم و این صد رکورد رو هم بر اساس تاریخ Sort میکنیم و برای کد شما هم یه شرط قرار میدیم شبیه این کد : 
ٌWhere R_Date <= @Date 

حالا مشکل اینجاست که ممنکنه در یه تاریخ ما 20 رکورد داشته باشیم با این تعاریف مانده حساب ما در تمام رکوردها با تاریخ های یکسان , یکسان خواهند بود .

----------


## JAFO_IRAN

سلام مجدد


 
query: 
  select record = cast(recnum as nvarchar(100)), debit, credit 
  from tbl_Src 
  order by record
record     debit    creit     sum(debit - credit)
------     -----    -----     -------------------
 1            10        0            10
 2             0       20           -10
 3            50        0            40
 4             0       10            30
 5             0       10            20
 6            50        0            70
 7             0       20            50
 8            10        0            60
 9            10        0            70
query: 
  select record = 'prev recs', debit = sum(debit), credit = sum(credit)
  from tbl_Src
  where recnum < 5
  union
  select record = cast(recnum as nvarchar(100)), debit, credit 
  from tbl_Src 
  where recnum >= 5
  order by recnum
record     debit    creit     sum(debit - credit)
------     -----    -----     -------------------
prev recs     60       30            30
 5             0       10            20
 6            50        0            70
 7             0       20            50
 8            10        0            60
 9            10        0            70

 


مثال اول برای وقتی است که محدوده نداریم و دومی با یک محدوده و union کار کرده:

خیلی مهمه که sum رو در گزارش درست کنیم نه در query - این جوری هر رکورد مانده مستقلی خواهد داشت.
به جای recnum میشه از تاریخ هم استفاده کرد - یا ترکیب تاریخ و شماره سند...

امیدوارم کمک باشه
ارادت

----------


## Amir_Safideh

باز هم سلام دوست گلم 
ممنون از این که اینقدر وقت میزاری . مشکل اصلی من همینه که من میخوام فیلد مانده رو توی یک DBGrid نمایش بدم .. قبلا سعی کردم که موقع گزارش گیری فیلد مانده رو بااستفاده از فیلدهای Calculated بسازم ولی نشد که در بالا گفتم که مشکل چی بود و چی شد . 
چیزی که من الان لازم دارم اینه که : 
من یه جدولی دارم که مثل تمام جداول شبیه خودش فیلدهای بدهکار و بستانکار داره و میخوام زمان گزارش گیری فیلد مانده رو هم در کنار بدهکار و بستانکار توی یک DBGrid نمایش بده .. البته در حال حاضر من از روش ایجاد Temp table استفاده میکنم که خدمتتون عرض کردم سرعتش در هنگام گزارشگیری پائینه چون تمام اطلاعات ثبت شده قبلی رو یکبار دیگر باید ثبت کنه و فیلد مانده رو بر اساس جمع تمام بدهکاران و بستانکاران قبلی محاسبه کنه که سرعت رو پائین میاره .
------------------------------
با تشکر از شما

----------


## JAFO_IRAN

سلام

P: خیلی عذر میخوام - اشتباه از من بود شدید (فکر کردم داستان درمورد fast report هستش). برای DBGrid اصولا روشی که من گفتم کار نمیکنه.

یک راه همون temp table هست که خودتون گفتید - اما راه دیگری که به ذهن من میرسه، استفاده از یک موجودی مثل client dataset برای بازنمایی اطلاعاته. اونجا میشه از internal calculated fields استفاده کرد و مثلا در AfterOpen یکبار مقدار فیلدها رو برای تمام سطرها تعیین کرد. میشه شبیه همون temp table، اما زمان پردازش از سرور به کلاینت منتقل میشه. در چاپ، چون همواره حالت unidirectional داریم میشد از اون روشی که من گفتم استفاده کرد ولی در نمایش در Grid باید به هر حال فیلد رو داشته باشیم... من باز هم روش فکر میکنم و اگه چیزی به نظرم اومد بهتون اطلاع میدم

ارادات

----------


## JAFO_IRAN

سلام

ببین این روش خیلی خوبیه

https://barnamenevis.org/showthread.php?t=37909

ارادت

----------


## Amir_Safideh

با سلام به دوست گلم که اسمت رو هم نمیدونم 
این روش رو من قبلا انجام داده بودم ولی مشکلش اینه که بر فرض اگر ما شرط را برای تابع Sum بر روی تاریخ اعمال کنیم(که دقیقا هم همین مورد لازمه)یعنی اگر بگیم که :

Select ......, (Select Sum(Field)
  From table_Inner.Date <= Table_Outer.Date)
From Table Table_Outer

اونوقت این مشکل پیش میاد که این این دستور Select جمع تمام رکوردهایی رو که این شرط براونها صادق باشه رو برمیگردونه حالا بر فرض اینکه ما 10 تا ثبت با یه تاریخ مشابه داشته باشیم در اونصورت تمام ثبتها با تاریخ مشابه دارای فیلد مانده با مقدار مشابه خواهند بود در صورتی که چیزی که عملا لازمه اینه که هر رکورد مانده واقعی خودش رو داشته باشه .
به همین دلیل بود که من از Temp Table استفاده کردم و در این Temp Table داده ها رو بر اساس تاریخ Sort کردم و یک فیلد Identity یا (AutoNum ) تعریف کردم به این ترتیب تمام ثبتها بر اساس تاریخ منظم میشن و یه فیلد AutoNum از ابتدا تا پایان ثبتها شماره گذاری میشه بعد برای بدست آوردن فیلد باقیمانده این کد رو برای Sum نوشتم :


Select ......,(Select Sum(....)
    From .....
    Where Table_Inner.Rank(AutoNum) <= Table_Outer.Rank(AutoNum))
From Table Table_Outer
 


به این ترتیب در مرحله اول داده ها بر اساس تاریخ منظم میشن بعدش دیگه فیلد AutoNum مبنای شرط ما قرار میگیره نه تاریخ به این ترتیب مشکل حل میشه ولی افسوس که این روش سرعت رو پائین میاره .

ولی از هر چی بگذریم من یه تشکر حسابی به شما بدهکارم که اینقدر وقت گذاشتی واقعا ازت ممنونم . باز هم منتظر راهنمائی های شما هستم .

-----------------

----------


## JAFO_IRAN

سلام

اولا تشکر و این حرفها لازم نیست قربان. باعث خوشحالی است و در ضمن این داستان قدیمی خیلی وقته که ذهن من رو به خودش مشغول کرده... هم فکری میکنیم دیگه...

ببین داستان مانده به صورت تئوری یک پیش نیاز اساسی داره: فقط وقتی میشه اونرو تعریف کرد که ترتیب دقیق باشه و هیچ دو رکوردی ارزش مرتب سازی مساوی نداشته باشند. اگه اینجوری به داستان نگاه کنیم، معلوم میشه که چرا تاریخ به تنهایی مشکل رو حل نمیکنه و چرا temp table شما کار کرده (چون اون AutoNum در نقش ترتیب دقیق قرار گرفته).

تنها تفاوت در عبارت شرطی در جدول داخلیه. مستندات مالی معمولا یک سریال هم دارند که در یک تاریخ ترتیبشون رو معلوم میکنه؛ تازه در نهایت اگه واقعا ارزش ترتیبی دو رکورد یکی باشن پس برای کاربر هم فرق نمیکنه که به چه ترتیبی بازنمایی بشن و مانده شون چج.ری محاسبه بشه - در این جور موارد حتی میشه از primary key برای درست کردن ترتیب دقیق در بین رکوردهای هم ارزش استفاده کرد.

مثلا اگه fld_Date تاریخ باشد و fld_Num شماره سریال (مثلا تکراری) . fld_Key کلید اصلی باشه میشه شرط داخلی را اینجوری نوشت:


select ....,
  (select Sum(...) from tbl_Src i
   where (i.fld_Date < o.fld_Date) or 
         ((i.fld_Date = o.fld_Date) and (i.fld_Num < o.fld_Num)) or
         ((i.fld_Date = o.fld_Date) and (i.fld_Num = o.fld_Num) and (i.fld_Key <= o.fld_Key)))
from tbl_Src o



ارادت

----------


## Amir_Safideh

دوست گلم دستت درد نکنه ..
این همون چیزیه که لازم داشتم چون من هم یه فیلد ID یا همون شماره سریال رو دارم و هم یه فیلد کلید با مقادیر یکتا .. با این شرطی که لطف کردی دیگه حله .
-----------
قربون شما .

----------

