19 January 2008

CRM 4.0 : Check current user's security role using JavaScript

It's a common question about how to show/hide fields based on user's security roles.
Ronald Lemmen had a very popular post on his blog about how to use 'RemoteCommand' to achieve that in CRM 3.0. Because 'RemoteCommand' is for internal use and unsupported, it doesn't work in CRM 4.0.
Michael H?hne also had a great post about how to access web service using client-side JavaScript. Since CRM 4.0 web service EndPoint changed, some people get 401 authorization error.

Here's code which works great in CRM 4.0, the function UserHasRole("ROLE_NAME") returns true if the current has the role, returns false if it doesn't. GetCurrentUserRoles() function generated by Michael H?hne's tool with some changes. Thanks for Regan who point out that using GenerateAuthenticationHeader() instead of hard-coding the Organization's Name.



//check if the current user has the 'System Administrator' role
alert(UserHasRole("System Administrator"));

function UserHasRole(roleName)
{
//get Current User Roles, oXml is an object
var oXml = GetCurrentUserRoles();
if(oXml != null)
{
//select the node text
var roles = oXml.selectNodes("//BusinessEntity/q1:name");
if(roles != null)
{
for( i = 0; i < roles.length; i++)
{
if(roles[i].text == roleName)
{
//return true if user has this role
return true;
}
}
}
}
//otherwise return false
return false;
}

function GetCurrentUserRoles()
{
var xml = "" +
"<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
"<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">" +
GenerateAuthenticationHeader() +
" <soap:Body>" +
" <RetrieveMultiple xmlns=\"http://schemas.microsoft.com/crm/2007/WebServices\">" +
" <query xmlns:q1=\"http://schemas.microsoft.com/crm/2006/Query\" xsi:type=\"q1:QueryExpression\">" +
" <q1:EntityName>role</q1:EntityName>" +
" <q1:ColumnSet xsi:type=\"q1:ColumnSet\">" +
" <q1:Attributes>" +
" <q1:Attribute>name</q1:Attribute>" +
" </q1:Attributes>" +
" </q1:ColumnSet>" +
" <q1:Distinct>false</q1:Distinct>" +
" <q1:LinkEntities>" +
" <q1:LinkEntity>" +
" <q1:LinkFromAttributeName>roleid</q1:LinkFromAttributeName>" +
" <q1:LinkFromEntityName>role</q1:LinkFromEntityName>" +
" <q1:LinkToEntityName>systemuserroles</q1:LinkToEntityName>" +
" <q1:LinkToAttributeName>roleid</q1:LinkToAttributeName>" +
" <q1:JoinOperator>Inner</q1:JoinOperator>" +
" <q1:LinkEntities>" +
" <q1:LinkEntity>" +
" <q1:LinkFromAttributeName>systemuserid</q1:LinkFromAttributeName>" +
" <q1:LinkFromEntityName>systemuserroles</q1:LinkFromEntityName>" +
" <q1:LinkToEntityName>systemuser</q1:LinkToEntityName>" +
" <q1:LinkToAttributeName>systemuserid</q1:LinkToAttributeName>" +
" <q1:JoinOperator>Inner</q1:JoinOperator>" +
" <q1:LinkCriteria>" +
" <q1:FilterOperator>And</q1:FilterOperator>" +
" <q1:Conditions>" +
" <q1:Condition>" +
" <q1:AttributeName>systemuserid</q1:AttributeName>" +
" <q1:Operator>EqualUserId</q1:Operator>" +
" </q1:Condition>" +
" </q1:Conditions>" +
" </q1:LinkCriteria>" +
" </q1:LinkEntity>" +
" </q1:LinkEntities>" +
" </q1:LinkEntity>" +
" </q1:LinkEntities>" +
" </query>" +
" </RetrieveMultiple>" +
" </soap:Body>" +
"</soap:Envelope>" +
"";

var xmlHttpRequest = new ActiveXObject("Msxml2.XMLHTTP");

xmlHttpRequest.Open("POST", "/mscrmservices/2007/CrmService.asmx", false);
xmlHttpRequest.setRequestHeader("SOAPAction"," http://schemas.microsoft.com/crm/2007/WebServices/RetrieveMultiple");
xmlHttpRequest.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
xmlHttpRequest.setRequestHeader("Content-Length", xml.length);
xmlHttpRequest.send(xml);

var resultXml = xmlHttpRequest.responseXML;
return(resultXml);
}



Due to Bloger's format issue, I have rewrite the code, it should work for everyone now.:)

56 comments:

Regan said...

Instead of manually piecing the authentication header together you should use the new javascript function GenerateAuthenticationHeader() which takes care of it all for you meaning you can leave out the hard-coding of the ORG_NAME stuff:)

Fit it in as follows:

...xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">" +
GenerateAuthenticationHeader() +
" <soap:Body...

Jim Wang said...

See we got something now. :)
Thanks Regan!
Jim

venu said...

Can you please post the entire script with a sample role name?

Thanks in advance

Anonymous said...

Should this code work in CRM 3.0? If so what could I have messed up, because it will not work for me.

cfellers said...

Jim,

I copied all of the code above into the OnLoad event for my main Account form and then changed the alert to look for the "Test" security role.

When I open an Account the alert always returns false, whether or not the current user is assigned to the "Test" role.

Is there any part of the code that I need to alter to be specific to my CRM 4.0 installation?

Hoho said...

I always get false as well. My alerting some of the workings, I can see roles.length is always equal to zero abnd thus no comparison to the given role is ever made.

Anonymous said...

the script return "false" for every role...
i'm missing something?

Anonymous said...

the script return "false" for every role...
i'm missing something?

Jim Wang said...

Answer some questions here:
1. It works for CRM 4.0 only, for CRM 3.0 security check, please check my post on the CRM Forum:

http://forums.microsoft.com/Dynamics/ShowPost.aspx?PostID=2773685&SiteID=27


2. For those who always get 'false' return(I known some of you didn't call the function ;)) please add a debug alert at:

function UserHasRole(roleName)
{
//get Current User Roles, oXml is an object
var oXml = GetCurrentUserRoles();
alert(oXml.xml); // add this debug alert


see what's you got!

Cheers,
Jim

Ben said...

Can you please post the entire script with a sample role name?

Thanks in advance

Aline said...

I've got 'false' return too. I've added debug alert like you do and recieved an empty alert message.
If you have any idea, please post it.
Thanks in advance

Ian said...

hi all,
i've added the alert as Aline has done and i get the same results. Any Suggestions?

Simon said...

I've got 'false' return too and if I show 'roles.length' using an alert comand it returns '0' elements...

Any suggestions???

Thanks a lot.

BigCalm said...

I'm having the same problem as everyone else - no results. After inserting this line:
alert("" + xmlHttpRequest.statusText);

after the
xmlHttpRequest.send(xml);

I'm getting "Bad request"

BigCalm said...

Got it - in the original post the RetrieveMultiple line ends in

"%22%3e"

This is wrong, it should end

\" >"

Anonymous said...

So how do you call the function?

IanR said...

Hi,

I altered the RetrieveMultiple line, as per bigcalm's advice, but still got no results...
So I added
alert(xmlHttpRequest.statusText);

straight after
xmlHttpRequest.send(xml);

(again, as per bigcalms previous post)
and got : Internal Server Error.

Am logged in as Administrator (am CRM System administrator)

Anybody got similar results or advice on how to fix?
thanks,

IanR said...

also...
I inserted

alert (oXml.xml)

and found out that I was getting an error 0x80040203 Invalid Argument

help???

thanks,

Anonymous said...

Thanks bigcalm...That fixed it for me...

Anonymous said...

Does anyone know if there is a page on the webserver that this code can be inserted into so that these objects can be called from any onload event?

Anonymous said...

Hi, Can you get user teams using the same code?

Simon said...

I have modified the RetrieveMultiple line like BigCalm has posted but I'm getting the same result:

---> "Internal Server Error"

---> 0x80040203

---> false

Any suggestion?

Anonymous said...

It fixed my problem as BigCalm said above.

Thanks

Priscilla said...

This was a lifesaver! Kudos.

Sam said...

How would you rewrite this code to include an if statement to check for user role instead of passing one in?

A. Chiesa said...

It's so smooth and clean!

Thanks man!

BTW: depending on your requests, you could need to cache the roles xml, in case you have to check for multiple roles.

I did this:

/////////////
var userRoles = null;

function UserHasRole(roleName)
{
if (!userRoles)
{
userRoles = GetCurrentUserRoles();
}
var oXml = userRoles;
if(oXml != null)
.....
.....
.....

This keeps you from double requesting user roles.

Keep up with the good work!

hero said...

Thanks a ton Jim, you saved a day for me

Howard said...

Hi Jim,
Thanks for the updated code, work great. I do have one question though, do you know why when I load the page it asks me to log in even though I already log into the system? It only asks to log in once though so if I close and open the screen again, it won't ask and know what my role is. Anyway to stop this log in screen from come up at all?

Thanks.

Anonymous said...

What does this look like in c# in plugin development. I want to do this on the server side.

Anonymous said...

I just posted how to do get the same security roles via C# for CRM server-side development. This was not well documented due to the many-to-many relationship.

http://jamiemiley.com/wordpress/?p=26

Mark Chaffee said...

Great Code. I been looking for this high and low for 6 hours now.

Mark Chaffee said...

Venu.

Try this.

//check if the current user has the '_AFM Acct. Rating Priv.' role
//alert(UserHasRole("_AFM Acct. Rating Priv."));

var oARC = crmForm.all.accountratingcode;

if(UserHasRole("_AFM Acct. Rating Priv."))
{
oARC.Disabled = false;
}
else
{
oARC.Disabled = true;
}

Put that before or after the main function.

Anonymous said...

thanks! just what i needed! you deserve to be MVP

. said...

酒店經紀PRETTY GIRL 台北酒店經紀人 ,禮服店 酒店兼差PRETTY GIRL酒店公關 酒店小姐 彩色爆米花酒店兼職,酒店工作 彩色爆米花酒店經紀, 酒店上班,酒店工作 PRETTY GIRL酒店喝酒酒店上班 彩色爆米花台北酒店酒店小姐 PRETTY GIRL酒店上班酒店打工PRETTY GIRL酒店打工酒店經紀 彩色爆米花

AsturRED said...

At first, sorry my English is no very good.

The first method, no is good for me. For me only run this.!!


alert(UserHasRole("Administrador del sistema"));


function UserHasRole(roleName)
{

var oXml = GetCurrentUserRoles();
alert(oXml.xml);
if(oXml != null)
{
alert(oXml .text);
var roles = oXml.selectNodes("//soap:Envelope/soap:Body/RetrieveMultipleResult/BusinessEntities/BusinessEntity") ;
var rol;
var valores;
if(roles != null)
{

alert(roles.length);
for( i = 0; i < roles.length; i++)
{
rol=roles[i];
valores=rol.selectSingleNode("q1:name");
if(valores.text == roleName)
{
return true;
}
}
}
}
return false;

}



But the second function, no run.
"Client server was unable to process request. Error 0x80040216"

Help me please, I dont undertand!!

G.Alvarez said...

here is the solution.

http://www.crowehorwath.com/cs/blogs/crm/archive/2008/05/08/hide-show-fields-in-crm-4-0-based-on-security-role.aspx

DigiOz Multimedia said...

Thank you very much for the code Jim. I was looking for those exact two functions, so you saved me a lot of work. Much appreciated.

Pete

Hojabri, Omid said...

Great Code!!!!
you solved my problem

咪摩兔 said...

八大娛樂全套-指油壓半套

高雄汽車借款.高雄當鋪.順利當鋪
高雄當鋪.高雄汽車借款.華旗當鋪
台中當鋪.台中汽車借款.全國當鋪
台中汽車借款.台中當鋪.利源當鋪

Jose said...

Hello, I am trying to do this on the orderprdouct page. I want check all itmems in orderproduct page if a value is checked. If one of the values are checked update the order form by updated a picklist field to a specific value.

Jose said...

Hello, I am trying to do this on the orderprdouct page. I want check all itmems in orderproduct page if a value is checked. If one of the values are checked update the order form by updated a picklist field to a specific value.

myfootprint said...
This comment has been removed by the author.
myfootprint said...

Hi,

Can use this script for hide/show tab by check security role. Such as
- System Administrator Show Tab
- Other role hide tab

Please help and thanks in advance

ajinkya utekar said...

Hi can i use for CRM2011

ajinkya utekar said...

HI,
How can i use it for CRM 2011

Thomas Trinh said...

Works great. Thank you.

Đào Quân said...

dich vu ke toan thue tron goi dích vu lam bao cao tai chinh tín
trung tâm đào tạo kế toán tại tphcm gia
khóa học kế toán thực hành re
công ty dịch vụ kế toán
dich vu ke toan thue tai bac ninh
dịch vụ kế toán trọn gói tại hà nội
dịch vụ kế toán tại tp.hcm
dịch vụ báo cáo thuế
dịch vụ quyết toán thuế uy
trung tâm đào tạo kế toán tại tphcm
trung tâm đào tạo kế toán tại cầu giấy tín
trung tâm đào tạo kế toán tại long biên
trung tâm đào tạo kế toán tại hà đông re
trung tâm kế toán tại thanh xuân
trung tâm kế toán tại bắc ninh
trung tâm kế toán tại bình dương
trung tâm kế toán tại hải phòng
dịch vụ quyết toán thuế tại quận bình thạnh
dịch vụ quyết toán thuế tại quận tân phú
dịch vụ quyết toán thuế tại quận 5
dịch vụ quyết toán thuế tại quận 3
dịch vụ quyết toán thuế tại tphcm
dịch vụ quyết toán thuế tại quận cầu giấy
dịch vụ quyết toán thuế tại quận long biên
dịch vụ quyết toán thuế tại quận hà đông
dịch vụ quyết toán thuế tại quận thanh xuân

Đào Quân said...

dịch vụ dọn dẹp sổ sách kế toán
dịch vụ dọn dẹp sổ sách kế toán tại thái bình
dịch vụ dọn dẹp sổ sách kế toán tại phú thọ
dịch vụ dọn dẹp sổ sách kế toán tại hưng yên
dịch vụ dọn dẹp sổ sách kế toán tại quận hải dương
dịch vụ dọn dẹp sổ sách kế toán tại hải phòng
dịch vụ dọn dẹp sổ sách kế toán tại quận thanh trì
dịch vụ dọn dẹp sổ sách kế toán tại quận hoàng mai
dịch vụ dọn dẹp sổ sách kế toán tại quận hai bà trưng
dịch vụ dọn dẹp sổ sách kế toán tại quận hoàn kiếm
dịch vụ dọn dẹp sổ sách kế toán tại quận từ liêm
dịch vụ dọn dẹp sổ sách kế toán tại quận ba đình
dịch vụ dọn dẹp sổ sách kế toán tại quận tây hồ
dịch vụ dọn dẹp sổ sách kế toán tại quận đống đa
dịch vụ dọn dẹp sổ sách kế toán tại bắc ninh
dịch vụ dọn dẹp sổ sách kế toán tại quận tphcm
dịch vụ dọn dẹp sổ sách kế toán tại quận cầu giấy
dịch vụ dọn dẹp sổ sách kế toán tại quận long biên
dịch vụ dọn dẹp sổ sách kế toán tại quận hà đông
dịch vụ dọn dẹp sổ sách kế toán tại quận thanh xuân
dịch vụ hoàn thuế gtgt

Đào Quân said...

dịch vụ báo cáo thuế tại tỉnh bình dương
dịch vụ báo cáo thuế tại quận bình thạnh
dịch vụ báo cáo thuế tại quận tân phú
dịch vụ báo cáo thuế tại quận 5
dịch vụ báo cáo thuế tại quận 3
dịch vụ báo cáo thuế tại tphcm
dịch vụ báo cáo thuế tại quận long biên
dịch vụ báo cáo thuế tại quận hà đông
dịch vụ báo cáo thuế tại quận thanh xuân
dịch vụ báo cáo thuế tại quận cầu giấy
dịch vụ báo cáo thuế tại gia lâm
dịch vụ báo cáo thuế tại đông anh
dịch vụ báo cáo thuế tại thanh trì
dịch vụ báo cáo thuế tại quận hoàng mai
dịch vụ báo cáo thuế tại quận hai bà trưng
dịch vụ báo cáo thuế tại quận từ liêm
dịch vụ báo cáo thuế tại quận hoàn kiếm
dịch vụ báo cáo thuế tại quận tây hồ
dịch vụ báo cáo thuế tại quận ba đình
dịch vụ báo cáo thuế tại quận đống đa

Đào Quân said...

dịch vụ làm báo cáo tài chính tại quận cầu giấy
dịch vụ làm báo cáo tài chính tại quận hai bà trưng
dịch vụ làm báo cáo tài chính tại quận ba đình
dịch vụ làm báo cáo tài chính tại thanh trì
dịch vụ làm báo cáo tài chính tại quận hoàng mai
dịch vụ làm báo cáo tài chính tại quận tây hồ
dịch vụ làm báo cáo tài chính tại quận đống đa
dịch vụ kế toán thuế tại quận cầu giấy
dịch vụ kế toán thuế tại quận thanh xuân
dịch vụ kế toán thuế tại bắc ninh
dịch vụ kế toán thuế tại quận hai bà trưng
dịch vụ kế toán thuế tại từ liêm
dịch vụ kế toán thuế tại hoàng mai
dịch vụ kế toán thuế tại ba đình
dịch vụ kế toán thuế tại thanh trì
dịch vụ kế toán thuế tại thái bình
công ty dịch vụ kế toán tại vĩnh phúc
công ty dịch vụ kế toán tại hưng yên
công ty dịch vụ kế toán tại phú thọ
công ty dịch vụ kế toán tại hải dương
công ty dịch vụ kế toán tại hải phòng
công ty dịch vụ kế toán tại bắc ninh
dịch vụ kế toán thuế tại vĩnh phúc
dịch vụ kế toán thuế tại hưng yên
dịch vụ kế toán thuế tại hải dương

Đào Quân said...

lớp học kế toán tổng hợp
lớp học kế toán thực hành
khóa học kế toán tổng hợp tại vinh nghệ an
trung tâm đào tạo kế toán tại vinh nghệ an
khóa học kế toán thực hành
Khóa học kế toán tổng hợp thực hành tại bắc ninh
Khóa học kế toán tổng hợp thực hành tại hải phòng
Khóa học kế toán tổng hợp thực hành tại tphcm
Khóa học kế toán tổng hợp thực hành tại bình dương
Khóa học kế toán tổng hợp thực hành tại hà đông
Khóa học kế toán tổng hợp thực hành tại cầu giấy
Khóa học kế toán tổng hợp thực hành tại long biên
Khóa học kế toán tổng hợp thực hành tại thanh xuân
dịch vụ kê khai làm báo cáo thuế hàng tháng
dịch vụ kế toán thuế trọn gói chuyên nghiệp giá rẻ
dịch vụ làm báo cáo tài chính giá rẻ
dịch vụ kế toán trọn gói chuyên nghiệp giá rẻ
dịch vụ rà soát dọn dẹp sổ sách kế toán chuyên nghiệp giá rẻ
dịch vụ quyết toán thuế chuyên nghiệp giá rẻ

Đào Quân said...

Lớp học kế toán tổng hợp thực hành tại hải phòng
Lớp học kế toán tổng hợp thực hành tại biên hòa đồng nai
Lớp học kế toán tổng hợp thực hành tại vinh nghệ an
Lớp học kế toán tổng hợp thực hành tại hải dương
Lớp học kế toán tổng hợp thực hành tại ninh bình
Lớp học kế toán tổng hợp thực hành tại hưng yên
Lớp học kế toán tổng hợp thực hành tại phú thọ
Lớp học kế toán tổng hợp thực hành tại hà nam
Lớp học kế toán tổng hợp thực hành tại vĩnh phúc
Lớp học kế toán tổng hợp thực hành tại bắc giang
Lớp học kế toán tổng hợp thực hành tại thái nguyên
Lớp học kế toán tổng hợp thực hành tại thái bình
Lớp học kế toán tổng hợp thực hành tại nam định
Lớp học kế toán tổng hợp thực hành tại thanh hóa
Lớp học kế toán tổng hợp thực hành tại tphcm
Lớp học kế toán tổng hợp thực hành tại bắc ninh
Lớp học kế toán tổng hợp thực hành tại hà đông
Lớp học kế toán tổng hợp thực hành tại long biên
Lớp học kế toán tổng hợp thực hành tại thanh xuân
Lớp học kế toán tổng hợp thực hành tại cầu giấy
khóa học kế toán toán dành cho giám đốc và nhà quản lý
công cụ dụng cụ
thủ tục thanh lý tài sản cố định
bảng cân đối kế toán
thuế thu nhập cá nhân năm 2015
phần mềm htkk 3.3.1

Đào Quân said...

hướng dẫn sử dụng hàm vlookup
thủ tục hoàn thuế thu nhập cá nhân
chi phí không được khấu trừ khi tính thuế tndn
chi phí không được khấu trừ khi tính thuế tndn
một số lỗi liên quan đến phần mềm htkk
cách kê khai bổ sung thế gtgt
cách lập bảng cân đối kế toán
cách lập báo cáo kết quả hoạt động kinh doanh

cách tính thuế gtgt theo phương pháp khấu trừ
xử lý hóa đơn sai
cách viết hóa đơn gtgt khi bán hàng
xử lý khi viết sai hóa đơn gtgt
lập tờ khai thuế gtgt mẫu 01
cách kê khai thuế gtgt theo tháng quý
cách tính thuế gtgt theo phương pháp trực tiếp
câu hỏi thường gặp về hóa đơn chứng từ
kê khai thuế tndn tạm tính quý
cách tính thuế gtgtg mới nhất
cách lập tờ khai thuế gtgt trên doanh thu 04
cách lập tờ khai thuế tndn 01b
ghi sổ kế toán theo hình thức nhật ký chung
xử lý khi phát hiện thừa thiếu hàng hóa
cách lập tờ khai thuế tndn 01a
xử lý khi phát hiện thừa thiếu hàng hóa

Đào Quân said...

căn cứ tính thuế đối với thu nhập từ đầu tư vốn
cách tính thuế đối với thu nhập từ chuyển nhượng vốn
cách khấu trừ thuế thu nhập cá nhân
hướng dẫn quyết toán thuế tncn
hướng dẫn hoàn thuế tncn
hướng dẫn thuế tndn
sửa đổi về thuế tndn
miễn thuế thu nhập cá nhân
người nộp thuế tncn là những ai
hoàn thuế gtgt
báo cáo thuế quý gồm những gì
cách lập hóa đơn gtgt
cách đăng ký thuế thu nhập cá nhân
lưu ý khi thực hiện quyết toán thuế tncn
hướng dẫn sử dụng một số hàm cơ bản trong excel
Một số sai sót thường gặp khi hạch toán kế toán
một số sai sót kế toán và cách sửa chữa
mẹo tô màu xen kẽ các dòng trên bảng tính lớn trong Excel
cách mở nhiều bảng tính khác nhau trong Excel
mức phạt nộp chậm thuế gtgt
quy định mới về hóa đơn bán hàng

Đào Quân said...

học kế toán thực hành cấp tốc
học kế toán thực hành cấp tốc
học kế toán thực hành tại cầu giấy
học kế toán thực hành tại thanh xuân
]học kế toán thực hành tại hà đông
học kế toán thực hành tại long biên
học kế toán thực hành tại long biên
học kế toán thực hành tại hải phòng
học kế toán thực hành tại bắc ninh
học kế toán thực hành tại tphcm
học kế toán thực hành tại quận 3
học kế toán thực hành tại hải phòng
học kế toán thực hành tại bắc ninh
học kế toán thực hành tại bình dương
học kế toán thực hành tại biên hòa
học kế toán thực hành tại vinh
học kế toán thực hành tại vinh
học kế toán thực hành tại huế
học kế toán thực hành tại đà nẵng
học kế toán thực hành tại đà nẵng
học kế toán thực hành tại đà nẵng
học kế toán thực hành tại hải dương
học kế toán thực hành tại hưng yên
học chứng chỉ kế toán
học kế toán ở đâu tốt

Đào Quân said...

thông tư 200
địa chỉ học kế toán
khóa học kế toán dành cho giám đốc
dịch vụ thành lập doanh nghiệp công ty trọn gói giá rẻ
trung tâm đào tạo kế toán tại hải dương
trung tâm đào tạo kế toán tại biên hòa đồng nai
lớp học kế toán tại biên hòa đồng nai
trung tâm đào tạo kế toán tại vinh nghệ an
trung tâm đào tạo kế toán tại quận 9
trung tâm đào tạo kế toán tại vĩnh phúc
trung tâm đào tạo kế toán tại hưng yên
trung tâm đào tạo kế toán tại phú thọ
trung tâm đào tạo kế toán tại bà rịa vũng tàu
trung tâm đào tạo kế toán tại quận 3 tphcm>trung tâm đào tạo kế toán tại quận 3
trung tâm đào tạo kế toán tại đà nẵng
trung tâm đào tạo kế toán tại huế
lớp học kế toán tổng hợp tại đà nẵng
lớp học kế toán tổng hợp tại huế
trung tâm đào tạo kế toán tại vinh nghệ an
trung tâm đào tạo kế toán tại đà nẵng
trung tâm đào tạo kế toán tại bà rịa vũng tàu
trung tâm đào tạo kế toán tại huế
khóa học kế toán dành cho người mới bắt đầu
khóa học kế toán dành cho người đã học kế toán