D365FO: Update non-editable table field with data entity - axapta

I have a following problem: I have a tableA (standard FO table) that has a fieldA (string) with the Allow Edit property set to to No. Now the user would like to have several fieldA values changed with an added suffix, for example: FOO -> FOO_bar.
Can I do some sort of an CSV import that has old and new values or what would be the best way for start solving this?

You can by code update the field irrespective of the fields AllowEdit property. This only affects its use in forms.
You can import a file as described here.
public void UploadFileData()
{
var fileUpload = File::GetFileFromUser() as FileUploadTemporaryStorageResult;
var file = AsciiStreamIo::constructForRead(fileUpload.openResult());
if (!file || file.status())
{
throw error("#SYS52680");
}
file.inFieldDelimiter(',');
file.inRecordDelimiter('\r\n');
for (var record = file.read(); !file.status(); record = file.read())
{
record = file.read();
if (record)
{
info(strFmt("%1 - %2",conPeek(record,1),conPeek(record,2)));
}
}
}

Related

Combine NSArrayController with NSTableview method "objectValueFor"

Can I use NSArrayController for my tableview , and using simultaneously this method : ?
func tableView(_ tableView: NSTableView, objectValueFor tableColumn: NSTableColumn?, row: Int) -> Any?
The idea behind:
I do not want to loose the benefits of the arraycontroller ( insert, update, delete ...) but I would like to have control on additional columns to display. The information inside these columns are calculated and formatted; values are coming from the array that the arraycontroller manages (Core Data).
I am afraid this is not possible because controller and tableviewfunction excludes each other ...
Thanks to Willeke, I got it made finally by using a extension for my entity.
extension ImportLog {
// Splits the imported lines into individual words
// For each entity, I split the property "line" .
// Later, in the objc computed property, I pick column number x
var splittedText:[String.SubSequence]{
return(self.line!.split(separator: ";"))
}
// col1
#objc var f1: String {
get {
let theColumn = 0
var text:String = ""
if ( splittedText.count-1 >= theColumn) {
text = String(splittedText[theColumn])
}
return text
}
set {
// no need to set something
}
}
}
The computed property "f1" of the entity can now be bound in the XIB file
by "Table Cell View.objectValue.f1"

Create a new record in form LedgerJournalTransCustPaym through x++ code

I need to create a reord in LedgerJournalTrans through x++ code.
While debugging I found out that the class LedgerJournalEngine_CustPayment is used to initiate the form as
LedgerJournalEngine_CustPayment = new LedgerJournalEngine_CustPayment(element)
and later
LedgerJournalEngine.initValue(LedgerJournalTrans);
also after assiging the accountNum the methods executed at the modified() method of datasource field LedgerJournalTrans:AccountNum are element.accountNumModifiedPost(); etc.
While trying to achieve the same through code I am not able to initiate the class LedgerJournalEngine_CustPayment and also the other methods in the form LedgerJournalTransCustPaym that system does.
Pls Help..
Joyce
LedgerJournalEngine* classes are mostly used by the forms to do work and execute code before/after events and datasource actions. What you're trying to do, it would probably just make more sense to complete all of the necessary ledgerJournalTrans fields, then do a .insert(). Here is some code I wrote that will do what you want though using the engine some:
static void Job81(Args _args)
{
LedgerJournalEngine_CustPayment ledgerJournalEngine;
LedgerJournalTable ledgerJournalTable;
LedgerJournalTrans ledgerJournalTrans;
NumberSeq numberSeq;
Voucher voucher;
;
// This just selects the header you are inserting into
select firstonly ledgerJournalTable where ledgerJournalTable.JournalNum == 'GB 0056226';
if (!ledgerJournalTable)
throw error ("Unable to find journal table record");
ledgerJournalTrans.initValue();
numberSeq = NumberSeq::newGetNumFromCode(ledgerJournalTable.VoucherSeries);
if (numberSeq)
{
ledgerJournalTrans.Voucher = numberSeq.num();
voucher = ledgerJournalTrans.Voucher;
}
ledgerJournalTrans.JournalNum = ledgerJournalTable.JournalNum;
ledgerJournalTrans.TransDate = SystemDateGet();
ledgerJournalTrans.AccountType = LedgerjournalACType::Cust;
ledgerJournalTrans.AccountNum = '100003';
ledgerJournalEngine = LedgerJournalEngine::construct(LedgerJournalType::CustPayment);
ledgerJournalEngine.newJournalActive(ledgerJournalTable);
ledgerJournalEngine.accountModified(ledgerJournalTrans);
ledgerJournalTrans.AmountCurCredit = 10;
ledgerJournalTrans.OffsetAccountType = ledgerJournalTable.OffsetAccountType;
ledgerJournalTrans.OffsetAccount = ledgerJournalTable.OffsetAccount;
ledgerJournalTrans.CurrencyCode = CompanyInfo::standardCurrency();
ledgerJournalEngine.currencyModified(ledgerJournalTrans);
ledgerJournalTrans.insert();
if (numberSeq && ledgerJournalTrans.Voucher == voucher)
{
numberSeq.used();
}
else
{
if (numberSeq)
numberSeq.abort();
}
info("Done");
}

Linq to entity delete a specific column from a table

Linq to entity query to delete a specific column from a table by matching a condition`
public ActionResult deleteChecks(string checkValue)
{
check_master checks = (from table in db.check_master
where table.check_code == checkValue
select table).First();
//now how to delete/remove checks.mcheck
return View("Edit");
}`
Only want to update a single column element with null value(of selected row) from the table check_master
You can set a single property (which maps to a column) to null and save it to the database
foreach(check_master check in checks)
{
check.mcheck = null;
}
db.SaveChanges();
using (NorthwindDataContext db = new NorthwindDataContext())
{
// Retrieve the existing entity. Database Call 1
Product product = db.Products.First(p => p.ProductID == 1);
// Change the properties. LINQ to SQL knows
// these specific properties have changed.
product.UnitsInStock = 14;
// Flush the changes. Database Call 2
db.SubmitChanges();
}
Entity framework works with constant table scheme only.
Tell please, what your global aim is, may be there's some more suitable way to do it.
Updated:
foreach(var chm in db.check_master)
{
chm.mcheck = null;
}
db.SaveChanges();
I believe that Linq to Entities only support DML, it does not support DDL operations.
So you would have to use stored procedure or ADO.NET raw query.
EDIT
you can do simple update like this :
public ActionResult deleteChecks(string checkValue)
{
check_master checks = (from table in db.check_master
where table.check_code == checkValue
select table).First();
checks.mcheck = null;
db.SaveChanges();
return View("Edit");
}`

How to get the form object in a ListPageInteraction class?

Working on Microsoft Dynamics AX 2012.
I have a listpage form which has a referenced ListPageInteraction class, just wanted to change the label / caption of a few control. For this I need to do something like:
element.form().design().control('<YourControlName>');
but I cant get this method on the ListPageInteraction class. I have decided to work on the class's initialized method. However there is no way to get to the form from there, how can I get to the controls and set labels?
common = this.listPage().activeRecord('Table');
if(common.isFormDataSource())
{
fds = common.dataSource();
fds.formRun().control(fds.formRun().controlId('ControlOfScreen')).
userPromptText('New Description');
}
Another example from projProjectTransListPageInteraction.initializeQuery() perspective changing the label of TransDate field from grid on form projProjectTransactionsListPage
public void initializeQuery(Query _query)
{
QueryBuildRange transDateRange;
// ListPageLabelChange =>
Common externalRecord;
FormDataSource frmDs;
FormRun formRun;
FormControl frmCtrl;
// ListPageLabelChange <=
;
queryBuildDataSource = _query.dataSourceTable(tableNum(ProjPostTransView));
transDateRange = SysQuery::findOrCreateRange(queryBuildDataSource, fieldNum(ProjPostTransView, TransDate));
// Date range is [(today's date - 30)..today's date] if not showing transactions for a particular project.
// Date range is [(dateNull())..today's date] if showing transactions for a particular project so that all transactions are visible.
transDateRange.value(SysQuery::range(transStartDate, systemDateGet()));
this.linkActive(_query);
// ListPageLabelChange =>
externalRecord = this.listPage().activeRecord(_query.dataSourceTable(tableNum(ProjPostTransView)).name());//No intrisic function for form DS?
if(externalRecord.isFormDataSource())
{
frmDs = externalRecord.dataSource();
formRun = frmDs.formRun();
if(formRun)
{
frmCtrl = formRun.design().controlName(formControlStr(projProjectTransactionsListPage,TransDate));
if(frmCtrl)
{
frmCtrl.userPromptText("newName");
}
}
}
// ListPageLabelChange <=
}
I don't think it is possible to get the FormRun object from ListPageInteraction.
If you were able to do it the rest would be easy:
FormControl fc = formRun.design().controlName(formcontrolstr(formName, controlName));
// etc.

Accessing the object/row being edited in Dynamic Data

I'm modifying the "Edit.aspx" default page template used by ASP.NET Dynamic Data and adding some additional controls. I know that I can find the type of object being edited by looking at DetailsDataSource.GetTable().EntityType, but how can I see the actual object itself? Also, can I change the properties of the object and tell the data context to submit those changes?
Maybe you have found a solution already, however I'd like to share my expresience on this.
It turned out to be a great pita, but I've managed to obtain the editing row. I had to extract the DetailsDataSource WhereParameters and then create a query in runtime.
The code below works for tables with a single primary key. If you have compound keys, I guess, it will require modifications:
Parameter param = null;
foreach(object item in (DetailsDataSource.WhereParameters[0] as DynamicQueryStringParameter).GetWhereParameters(DetailsDataSource)) {
param = (Parameter)item;
break;
}
IQueryable query = DetailsDataSource.GetTable().GetQuery();
ParameterExpression lambdaArgument = Expression.Parameter(query.ElementType, "");
object paramValue = Convert.ChangeType(param.DefaultValue, param.Type);
Expression compareExpr = Expression.Equal(
Expression.Property(lambdaArgument, param.Name),
Expression.Constant(paramValue)
);
Expression lambda = Expression.Lambda(compareExpr, lambdaArgument);
Expression filteredQuery = Expression.Call(typeof(Queryable), "Where", new Type[] { query.ElementType }, query.Expression, lambda);
var WANTED = query.Provider.CreateQuery(filteredQuery).Cast<object>().FirstOrDefault<object>();
If it's a DD object you may be able to use FieldTemplateUserControl.FindFieldTemplate(controlId). Then if you need to you can cast it as an ITextControl to manipulate data.
Otherwise, try using this extension method to find the child control:
public static T FindControl<T>(this Control startingControl, string id) where T : Control
{
T found = startingControl.FindControl(id) as T;
if (found == null)
{
found = FindChildControl<T>(startingControl, id);
}
return found;
}
I found another solution, the other ones did not work.
In my case, I've copied Edit.aspx in /CustomPages/Devices/
Where Devices is the name of the table for which I want this custom behaviour.
Add this in Edit.aspx -> Page_Init()
DetailsDataSource.Selected += entityDataSource_Selected;
Add this in Edit.aspx :
protected void entityDataSource_Selected(object sender, EntityDataSourceSelectedEventArgs e)
{
Device device = e.Results.Cast<Device>().First();
// you have the object/row being edited !
}
Just change Device to your own table name.

Resources