الانتقال إلى المحتوى

عمل ريبورت ديناميك


mw_wageeh

Recommended Posts

السلام عليكم
اقدم هذة المشاركة وارجو ان يشارك فيها الزملاء وان تكون فعالة ويستفاد منها الجميع
هقدم لكم ازاى نعمل ريبورت يقرأ من أكثر من تيبل
بمعنى
عندى اكثر من تيبل وليكن
(emp1,emp2,emp3,emp4)
ونفس ال
structure
على حسب البارمتر اللى هيرسل للتقرير من الفورمة الريبورت هيقرأ من اى تيبل
بمعنى لو ارسلت 1 التقرير يقرأ من emp1
لو ارسلت 2 التقرير يقرأ من emp2
لو ارسلت 3 التقرير يقرأ من emp3
لو ارسلت 4 التقرير يقرأ من emp4
معظمنا بيعمل 4 تقارير ويريح نفسة وعلى حسب البارمتر يقرأ التقرير اللى هوة عايزة
الطريقة دى اسهل الطرق ولكن مشاكلها اكبر وهى لو انطلب تعديل فى الريبورت هتجبر انك تعدل فى ال 4 تقارير الاخرى
المطلوب دلوقتى عايزين نعمل ريبورت واحد ويقرأ من التيبل اللى احنا عازينة على حسب البارمتر المرسل له من الفورمة
عايزين 4 طرق نعمل بيها الريبورت
لو اى عضو عندة طريقة يضعها هنا
وبأذن الله هضع كل يوم طريقة واتمنى من باقى الاعضاء المشاركة والمناقشة
انتظرو أول طريقة غدا

رابط هذا التعليق
شارك

السلام عليكم
ياريت ياشباب يبقى فية تفاعل أكتر من كدة
عامة دى اول طريقة
هرسل بارمتر من الفورمة وليكن اسمة
p
وعلى حسب قيمة البارمتر
هنفذ اى كيورى
يعنى
لو ارسلت البارمتر ب 1
هينفذ اول سيليكت
ولو ارسلت البارمتر ب 2 هينفذ تانى سيليكيت
وهكذا

[/b]

select empno,ename,job
from emp1
where p=1
union
select empno,ename,job
from emp2
where p=2
union
select empno,ename,job
from emp3
where p=3
union
select empno,ename,job
from emp4
where p=4

[b]



وانتظرو الطريقة الثانية غدا باذن الله

تم تعديل بواسطة mw_wageeh
رابط هذا التعليق
شارك

وعليكم السلام ورحمة الله

شكرا لك على مناقشة هذا الاسلوب فى طباعة التقارير


سوف اكون من المتابعين معك .... ان شاء الله


لانه توجد افكار كثيرة حول هذا الموضوع


جزاك الله خيرا

رابط هذا التعليق
شارك

فكرة رائعة يا ابو احميد وشكرا علي الدعوة البريدية وسلامي للاخ المحترم أمجد
وبالتوفيق والي الامام دوما


أحمد

رابط هذا التعليق
شارك

السلام عليكم ورحمة الله وبركاته

الطرق الاربعة التي سأرسردهم ترتكز علي امرار متغيرات من الشاشة الي التقرير المنادي

امرار data record من الشاشة الي التقرير طريقة فعاله الي حد كبير حيث يتم استخدام record cache أو ما يعرف ب record group بشرط أن تكون أسماء ,انواع الحقول في التقرير هي اسمائها في record group وفي التقرير يمكنك عمل query based on dual مثل

select col1,col2,col3,col....x from dual

هذا مع الاخذ في الاعتبار أن ال query المناداة لا يمكن أن تمرر ل child query بل ل master query فقط لكن للعلم يمكن تغيير قيم chlid query بامكانية امرار where clause كما سنري لاحقا في الخطوات

1-الطريقة الاولي والمعروفة
مناداة ما يعرف ب Bind Parameter

يمكنك عمل تقرير بسيط وليكن كما يلي
Q_emp: SELECT empno, sal
FROM emp
WHERE deptno = :departmentno
ORDER BY sal

داخل الشاشة قم بعمل procedure اسمه pass_parameter والذي يمكن وضعه علي مفتاح لمنادة التقرير
هذا ال procedure العزيز سوف يقوم بامرار متغيرين أحدهما هو رقم القسم والثاني لايقاف عرض متغير الشاشة وسيتم امرار المتغيرين باستخدام ال parameter list والكود كما يلي

PROCEDURE pass_parameter
IS
plid paramlist;
the_param varchar2(15) := 'tmpdata';
BEGIN
plid := get_parameter_list(the_param);

/* check if 'tmpdata' exists */

IF NOT id_null(plid) THEN
destroy_parameter_list(plid);
END IF;

/* if it does destroy it */

plid := create_parameter_list(the_param);

/* create it afresh */

add_parameter(plid, 'departmentno', TEXT_PARAMETER,to_char(:dept.deptno));

/* associate the param in the form with the param in the report */

add_Parameter(plid, 'PARAMFORM', TEXT_PARAMETER, 'NO');

/* to suppress the parameter form displaying */

run_product(REPORTS, /* product name */
'formrep.rdf', /* Oracle Reports module */
SYNCHRONOUS, /* communication mode */
RUNTIME, /* execution mode */
FILESYSTEM, /* location of the Reports module */
plid, /* handle to the parameter list */
null
);
END;



وهذه الطريقة هي المتعارف عليها لامرار متغيرات بطريقة عادية

2- الطريقة الثانية

في التقرير سنقوم بعمل متغير parameter اسمه MYWHERE ونعطيه قمية افتراضية وليكن where 1=1
امرار ما يعرف بالمتغيرات المعجمية ( ذات علاقة بمفردات لغوية ) وهي ما تعرف ب Lexical Parameters ويقوم بارسال string من الشاشة للتقرير
مثل
where loc like 'NEW YORK'

قم بعمل simple query مثل
Q_emp: SELECT empno, sal
FROM emp
&MYWHERE

ثم من الشاشة قم بعمل ال procedure المسمي pass_parameter

PROCEDURE pass_parameter
IS
plid paramlist;
the_param varchar2(15) := 'tmpdata';
clause varchar2(60);
BEGIN
plid := get_parameter_list(the_param);

/* check if 'tmpdata' exists */

IF NOT id_null(plid) THEN
destroy_parameter_list(plid);
END IF;

/* if it does destroy it */

plid := create_parameter_list(the_param);

/* create it afresh */

clause := '"'||'where loc like '||''''||'NEW YORK'||''''||'"';
add_parameter(plid, 'MYWHERE', TEXT_PARAMETER, clause);

/* associate the param in the form with the param in the report */

add_Parameter(plid, 'PARAMFORM', TEXT_PARAMETER, 'NO');

/* to suppress the parameter form displaying */

run_product(REPORTS, /* product name */
'formrep.rdf', /* Oracle Reports module */
SYNCHRONOUS, /* communication mode */
RUNTIME, /* execution mode */
FILESYSTEM, /* location of the Reports module */
plid, /* handle to the parameter list */
null);
END;




3-الطريقة الثالثة هي امرار اخر query تم اجراؤها من الشاشة الي التقرير

في التقرير نقوم الان بعمل lexical parameter وليكن
MYWHERE (initial value is: where 1=2)
وال query كالتالي
Q_emp: SELECT * FROM emp &MY_WHERE

في جالشاشة سنقوم بعمل ال procedure والجميل هنا أنه سيقوم باختبار اذا كانت تحتوي ال query الاخيرة المجراه في الشاشة يشتمل علي where caluse or not

PROCEDURE pass_where IS
plid paramlist;
the_param varchar2(15) := 'tmpdata';
the_query varchar2(1000); /* store the query */
i number(4); /* will hold the position of WHERE */
BEGIN
plid := get_parameter_list (the_param);

if not id_null(plid) then
destroy_parameter_list(the_param);
end if;

plid := create_parameter_list(the_param);

the_query := :system.last_query;

/* find the position of the first instance of the word where */

i := instr(the_query,'WHERE');

/* check, if the query contains a WHERE clause, i=0 if not */
/* update the_query with just the contents of the where clause */

IF i > 0 THEN
the_query := substr(the_query, i, length(the_query) - i + 1);
ELSE
the_query := ' ';
END IF;

add_parameter(plid, 'MYWHERE', TEXT_PARAMETER, the_query);
add_Parameter(plid, 'PARAMFORM', TEXT_PARAMETER, 'NO');

run_product(REPORTS,
'formrep.rdf',
SYNCHRONOUS,
RUNTIME,
FILESYSTEM,
plid,
null
);
end;



4- وصلنا بحمد الله لما يقصده الاخ الفاضل المهندس محمد وجيه وهو تغيير ال query داخل التقرير اصلا ليكون متجدد وفي هذه الطريقة
نقوم بعمل تقرير مبني علي single query بدون where condition لنري تأثير مناداة ال record group
Q_emp: SELECT empno, sal
FROM emp

في الشاشة
نقوم بعمل ال procedure ومناداته من push botton

Create the Oracle Forms procedure outlined below as a PL/SQL program
units and reference it from a when-button-pressed trigger.

PROCEDURE pass_record
IS
repquery varchar2(15) := 'q_emp';
/* name of the reports query */
queryrec varchar2(15) := 'recgroup';
/* name of the record group */
the_param varchar2(15) := 'tmpdata';
plid paramlist;
rgid recordgroup;
the_query varchar2(1000); /* store query in this */
return_cd number;
BEGIN
the_query := 'SELECT empno, sal FROM emp WHERE deptno='||
to_char(:dept.deptno)||' ORDER BY sal';

/* create the query string */

rgid := find_group(queryrec);

/* get handle to record group */

IF id_null(rgid) THEN
rgid := create_group_from_query(queryrec, the_query);
END IF;

/* creating group from query, if it wasnt found */

delete_group_row(rgid,all_rows);

/* empty the record group */

return_cd := populate_group_with_query(rgid,the_query);

/* populate record group using the defined query */

plid := get_parameter_list(the_param);

/* check if 'tmpdata' exists */

IF NOT id_null(plid) THEN
destroy_parameter_list(plid);
END IF;

/* if yes - destroy it */

plid := create_parameter_list(the_param);

/* create it and associate the record group with the query in Reports */

add_parameter(plid, repquery, DATA_PARAMETER, queryrec);

add_Parameter(plid, 'PARAMFORM', TEXT_PARAMETER, 'NO');

/* to suppress the parameter form displaying */

run_product(REPORTS, /* product name */
'formrep.rdf', /* Oracle Reports module */
SYNCHRONOUS, /* communication mode */
RUNTIME, /* execution mode */
FILESYSTEM, /* location of the Reports module */
plid, /* handle to the parameter list */
null);
END;




انتهي ما لدي عن ما همته بهذا الصدد وبقي أن أقول بالنسبة ل record group
يمكن طبعا أن تؤلف من أربع جداول ومن ثم امرار ال query حسب الطريقة الرابعة وأرجوا ان تكون قد وصلت الافكار وبالتأكيد كما تفضل اخي أمجد هناك افكار عديدة

لكن ما يهمني الان انه بما أنك قد توصلت لامرار record group من الشاشة للتقرير طيب اخذ رأيكم لو قمنا بتطوير الفكرة لعمل dynamic record group ثم مناداته لتكون العملية كلها باذن الله completed dynamically automated

لعمل Dynamic creation of record group

DECLARE
 ora_err NUMBER;
 rg_id   RECORDGROUP;
BEGIN

 rg_id := CREATE_GROUP_FROM_QUERY('CGLL$POP_LIST','
SELECT LIBELLE , CODE
FROM ( SELECT column1 CODE,
   column2 LABEL
FROM your_table_name
WHERE   ( your_where_clause) ) ');

 /* Populate the record group by executing the query */
 ora_err := POPULATE_GROUP(rg_id);

/* Intitialise the list item with the record group */
POPULATE_LIST('VU.CODE_GENRE', rg_id);

 /* Always delete the record group */
 DELETE_GROUP(rg_id);

END;



يمكن ايضا الاستعانه ب built ins مثل
CREATE_GROUP_FROM_QUERY, POPULATE_GROUP and SET_LOV_PROPERTY()

من فوائد dynamic record group عمل list item معتمد علي البيانات Dynamic list item وكذلك عمل LOV

لنري ذلك

PROCEDURE POPULATE_LIST_WITH_QUERY
( P_LIST IN VARCHAR2,
P_QUERY IN VARCHAR2)
IS
RG_NAME VARCHAR2(30):='P_RG_NAME';
R_GROUP RECORDGROUP;
STATUS NUMBER;
C VARCHAR2(30):=GET_ITEM_PROPERTY(P_LIST,ITEM_NAME);
BEGIN
R_GROUP:= FIND_GROUP(RG_NAME);

IF NOT ID_NULL(R_GROUP) THEN
DELETE_GROUP(R_GROUP);
END IF;

R_GROUP := CREATE_GROUP_FROM_QUERY( RG_NAME,P_QUERY);

IF POPULATE_GROUP(R_GROUP)= 0 THEN
POPULATE_LIST(P_LIST,R_GROUP);
COPY(GET_LIST_ELEMENT_VALUE(P_LIST,1),P_LIST);
END IF;
END;


ثم يمكنك في new form instance منادة هذا ال procedure كما يلي
POPULATE_LIST_WITH_QUERY('emp.dname',
'select dname,to_char(deptno) from dept');

حيث أنك قمت مسبقا بعمل list item علي emp.dname lj متذكرا أن القيمة الاولي سوف تعرض علي List item أما الثانية ستخزن في البيانات وللعلم العمودين يجب أن يكونوا من النوع char

أعلم أن الجزء الاخير ليس في صميم المضوع انما بالمرة أحببت أن أفيد الاخوة المبتدئين كيفية عمل dynamic list item للصلة مع الموضوع الاصلي والرابط هو record Group

الاخير هو استغلال ال dynamic record group في عمل List of Values ويمكن ملاحظة الكود التالي

DECLARE
rg_name VARCHAR2(40) := 'COMPANY_RANGE';
rg_id RecordGroup;
errcode NUMBER;
lov_id LOV;
BEGIN
:System.Message_Level := 0;
rg_id := Find_Group( rg_name );
IF Id_Null(rg_id) THEN

rg_id := Create_Group_From_Query( rg_name||'_TMP', 'select COMPANIES_ID from SYSMODULE.COMPANIES where 1=2');
Set_LOV_Property(lov_id,GROUP_NAME,rg_name||'_TMP');

rg_id := Create_Group_From_Query( rg_name, 'select COMPANIES_ID from SYSMODULE.COMPANIES');
IF Id_Null(rg_id) THEN
Message(' Create group failed');
Message(' ',no_acknowledge);
Raise Form_trigger_failure;
End if;
END IF;
errcode := Populate_Group( rg_id );
lov_id := Find_LOV('LOV7');
Set_LOV_Property(lov_id,GROUP_NAME,rg_name);
END;



بالتوفيق وأشكرك أخي وجيه علي توجيه الدعوة عبر البريد للمشاركة وأتمني أن يسرد افكار جديدة من الاخوة الافاضل

أحمد عبد المجيد

تم تعديل بواسطة mageed_ahmed
رابط هذا التعليق
شارك

السلام عليكم
شكرا لاخوى امجد واحمد
وشكر كثير جدا جدا لاستاذى المهندس احمد عبدالمجيد وجزاك الله بكل حرف حسنة

فعلا ارسال الريكورد جروب الى الريبورت من الطرق الشيقة والقوية ولم اعرفها الا من شهرين
وكنت ناوى اشرحها فى الطريقة الرابعة


الطريقة الثانية
1- هنعمل تيبل جديد على الداتا بيز من نوع temp وليكن
emp_temp
ومعنى تيبل من نوع temp ان التيبل بيفضى الداتا اللى فية لما اعمل commit او فى حالة انى اخرج من session
2- هنعمل بروسيدر على الداتا بيز هتملآ تيبل ال emp_temp

CREATE  GLOBAL TEMPORARY TABLE emp_temp
(
 EMPNO     NUMBER(4),
 ENAME     VARCHAR2(10 BYTE),
 JOB       VARCHAR2(9 BYTE),
 MGR       NUMBER(4),
 HIREDATE  DATE,
 SAL       NUMBER(7,2),
 COMM      NUMBER(7,2),
 DEPTNO    NUMBER(2)
)



2-
كود البروسيدر
d

CREATE OR REPLACE PROCEDURE fill_emp(p number) IS
BEGIN IF P = 1 THEN
  INSERT INTO emp_temp SELECT * FROM EMP1  ;
  
  ELSIF P = 2 THEN
     INSERT INTO emp_temp SELECT * FROM EMP2  ;
 
 ELSIF P = 3 THEN
     INSERT INTO emp_temp SELECT * FROM EMP3  ;

 ELSIF P = 4 THEN
     INSERT INTO emp_temp SELECT * FROM EMP2  ;
 END IF ;

END fill_emp;


a

هنعمل ريبورت جديد والريبورت هيقرأ من تيبل
emp_temp
ودة الكيورى بتاع الريبورت
select * from emp_temp
هتقولى التيبل فاضى
مهوة انا هملا التيبل دة باالبروسيدر اللى انا عملتها وهى
fill_emp
بس هناديها امتة
هناديها فى التريجر
BEFORE REPORT
وهبعتلها البارمتر اللى مرسل من الفورمة
وعلى حسب البارمتر البروسيدر هتملا تيبل ال EMP_TEMP

ملحوظتين
1- فية ناس هتقول معمل تيبل عادى بدل معمل
TEMPORARY TABLE
واحذف بيانات التيبل كل مملاأ البروسيدر
اوك مش هيحصل مشاكل بس المشكلة هتحصل لما شخص اخر بيشغل نفس الريبورت
ساعتها هتحذف البيانات بتاعتة وهيطلعة الريبورت فارغ

2- فية ناس هتقول انادى على البروسيدر من الفورمة بدل مناديها من الريبورت وبعدين انادى الريبورت
دة غلط لازم انادى البروسدير من الريبورت
لانك ساعتها هتفتح سيشن جديدة وساعتها مش هتشوف الداتا اللى فى التيبل

الطريقة دى قوية جدا عندما اعمل ريبورت معقد وبيقرأ من تيبل كتيرة وفية حسابات كتيرة
فبريح نفسى واعمل بروسيدر على الداتا بيز واعمل كل الحسابات وكل المعادلات وبعدين ادخلها فى التيبم تيبل
وممكن اعمله ابديت فى شروط معينة وفى الاخر تعمل ريبورت بسيط جدا وهية مجرد جملة سيليكت عادية جدا تقرأ من تيبل ال temp


كدة شرحنا طريقتين والمنهدس أحمد عبدالمجيد شرح طريقة
فانتظرو الطريقة الاخيرة

تم تعديل بواسطة mw_wageeh
رابط هذا التعليق
شارك

السلام عليكم
الطريقة الرابعة وهى استعمال ref cursor
فى اداة اسمها ref cursor query
بجانب sql query على الريبورت على ال data model
تقريبا معظمنا ميعرفشى اية الاداة دى
الاداة دى بتسمحلى اكون اى كيورى انا عايزة بشرط كل كيورى متساوى فى عدد الكوليمن ونفس الداتا تايب للكولمن تكون متشابها
يعنى اعمل كيورى مرة من emp1 بكوليمن وشروط معينة
واعمل كيورى اخر من emp2 بكوليمن وشروط تانية خالص
طريقة
دلوقتى انا عايز اعمل ريبورت بس هيكون ديناميك شوية عايز اعمل ريبورت يعرضلى
a-اسم الموظف ثم المرتب بتاعة
b-رقم الادارة ومجموع مرتبات كل ادارة
c-اسم الوظيفة ومجموع مرتبات كل وظيفة
الريبورتات دى سهلة جدا بس معظمنا هيعملها على 3 ريبورت وهينادى على كل ريبورت
لا احنا عايزين نعلمها فى ريبورت واحد بس
1- هكون جملة السيليكت على الفورم ممكن نعمل السيلكتات دى

a-p_query := 'select ename,sal sal from emp' ;
b-p_query := 'select to_char(deptno),sum(sal) sal from emp group by deptno' ;
c-p_query := 'select job ,sum(sal) sal from emp groub by  job' ;




خودو بالكم ال 3 سيلكتات متساويين فى عدد الكوليمن ونفس الداتا تايب
هنرسل البارمتر p_query للريبورت على حسب ما عايز ابعت اى كيورى

-هنعمل باكدج على الداتا بيز

CREATE OR REPLACE PACKAGE pk_refcur
IS
   type my_rec is record (ename     varchar2 (40)
                       ,detail     varchar2 (40)
                         );

 type cv_refcur   is ref cursor return my_rec;
 type  dyn_refcur  is ref cursor;
 function fn_ret_refcur (v_sql varchar2) return dyn_refcur;

END; 
/

ودة ال body
CREATE OR REPLACE PACKAGE BODY pk_refcur
IS
function fn_ret_refcur (v_sql  varchar2)
return dyn_refcur
is
v_cv_refcur     pk_refcur.dyn_refcur;      
begin open v_cv_refcur for v_sql;
return v_cv_refcur;

end fn_ret_refcur;

END; 
/




3- ههنشىء الريبورت نسحب الاداة اللى اسمها ref cursor query والتى بجانب sql query
اول مهنضعها هيعملى فانكشن اتوماتيك وهكتب بداخلها الكود دة

function Q_RefCurDS 
return pk_refcur.cv_refcur
is
  temp_v_my_rec  pk_refcur.cv_refcur;
begin temp_v_my_rec := pk_refcur.fn_ret_refcur (_sql);
  return temp_v_my_rec;
 
end;



وبعد كدة حاول تشغل الريبورت
وبكدة نكون خلصنا ال 4 طرق
عارف ان الطريقة دى غلسة شوية بس قوية جدا

واى استفسار فى اى طريقة ممكن يراسلنى وانا فى الخدمة

تم تعديل بواسطة mw_wageeh
رابط هذا التعليق
شارك

ما شاء الله عليك

أرجوا من الاخ المشرف الموقر بتثبيت هذا الموضوع لما له من أهميه فهو مرجع حقيقي لمن أراد أن يغير من طبيعة تقاريره

نشكر الاخ المهندس محمد وجيه علي فكرته ومجوده الرائعين مثله وفي انتظار ما هو أفضل


احياتي

رابط هذا التعليق
شارك

بارك الله فيكم على أسلوب الشرح الرئع

رابط هذا التعليق
شارك

  • بعد 5 شهور...
  • بعد 1 شهر...
  • بعد 3 أسابيع...
  • بعد 11 شهور...

بارك الله في الجميع
ولكن أريد أن أسأل كيف يمكنني عمل تقرير في أوراكل 10جي كصفحة ويب يتم تحديث البيانات فيه دون الاضطرار إلى تشغيل أو استدعاء التقرير مرة ثانية. يعني أريد أن أضغط على زر التحديث في الإكسبلورر مثلا فيعطيني آخر تعديلات في قاعدة البيانات
وشكرا لكم

رابط هذا التعليق
شارك

السلام عليكم
بالنسبة لسؤال الاخ كمال سؤالك غير مدعم فى اوراكل
يجب مناداة الريبورت مرة اخرى
ممكن لو عايز الداتا تتحدث ممكن تعمل الداتا اللى بتظهر على الريبورت يكون على الفورمز
يعنى بدل متعمل ريبورت اعمل فورمة

رابط هذا التعليق
شارك

  • بعد 1 شهر...
  • بعد 1 شهر...
  • بعد 4 شهور...

بسم الله الرحمن الرحيم

اخواني الاعزاء

الموضوع بسيط كثير

يتم عمل parameter اسمه tname

ويتم وضع initial Value للباراميتر
emp1

ثم يتم عمل Data Model
بنكتب فيه

Select * from &tname

ويتم تغيير اسم الجدول من نفس الباراميتر عند التنفيذ

رابط هذا التعليق
شارك

  • بعد 1 سنة...
  • بعد 4 شهور...
  • بعد 7 شهور...
  • بعد 2 سنة...

 

الرد متاخر  6 سنين لكن ببساطة الطريق سهلة جدا وهي عمل view كالاتي :

(create or replace view emp (col1,col2,col3,.......,parmaeter

as 

select col1,col2,col3,........,1

from emp1

union 

select col1,col2,col3,........,2

from emp2

union

select col1,col2,col3,........,3

from emp4

union 

select col1,col2,col3,........,4

from emp4

 

وتعمل تقرير واحد تبعت لة البارمتير الرقم  وتكون الكويري تقراء طبعا من الview

وشكرا

 

رابط هذا التعليق
شارك

  • بعد 11 شهور...

انضم إلى المناقشة

يمكنك المشاركة الآن والتسجيل لاحقاً. إذا كان لديك حساب, سجل دخولك الآن لتقوم بالمشاركة من خلال حسابك.

زائر
أضف رد على هذا الموضوع...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   تمت استعادة المحتوى السابق الخاص بك.   مسح المحرر

×   You cannot paste images directly. Upload or insert images from URL.

جاري التحميل
×
×
  • أضف...

برجاء الإنتباه

بإستخدامك للموقع فأنت تتعهد بالموافقة على هذه البنود: سياسة الخصوصية