Why we disagree on politics

Today we’re going to take on a different subject not related to programming… that’s right, we’re going to talk about politics.  I will not discuss current events or current leaders; this post will not become dated.  Rather, I’d like to take a step back and discuss why politics exists.  Why do some people decide one thing, and other people are fervently decided against it?  There is of course some self-interest, but it doesn’t take long to see that self-interest only influences a few issues, and generally isn’t responsible for all the passion.  So what causes passionate politics?  People become passionate when they think that they are in the right.  So it all comes down to two questions:  “How do we decide what is right?  On what basis do we determine what is moral?

I’ve been reading Psychologist Jonathan Haidt’s research and he did a study on personal morality that determines that people make moral judgement based on five factors.  He self-identifies as a liberal, so to have a balanced view I read some conservative articles as well, which came up with a sixth factor.  Here they are:

  1. Harm/Care.  Will this help us taking care of our children, our elderly?  Or will it put people in danger?
  2. Fairness/Reciprocity.  Is it equal and just for all?  Is everyone getting their fair due?
  3. Ingroup Loyalty.  Is this going to betray our friends?  Or will we be showing solidarity?
  4. Authority/Respect.  Does this demonstrate respect and awe for those in power, or does it encourage dissent and anarchy?
  5. Purity/Sanctity.  Is it nasty, or wholesome?  Is it holy or an abomination?
  6. Liberty.  Does this preserve someone’s rights, or does it infringe on them?

Haidt found that people who self-identify as liberal tend to care mostly about the first two issues – care and fairness.  In contrast, conservatives feel more or less equally about the first five issues.  Lastly, libertarians emphasize liberty higher than any other factor.

So what effect does this have on our discourse?  When a liberal and a conservative discuss an issue, the liberal is going to come at it from two angles – Harm and Fairness.  The conservative has five angles – harm, fairness, loyalty, respect for authority, and purity.

The result is they speak past each other.  The liberal feels that the conservative is bringing up a lot of distractions that just don’t matter.  He feels that the conservative is coming up with excuses to let needy people down and treat people unfairly.  He determines that conservative must not truly care about people and must secretly hate large sections of society.  Ultimately, the liberal finds himself believing that conservatives are evil.

The conservative is able to understand the liberal’s care and fairness arguments, but he doesn’t understand why the liberal ignores concerns about loyalty, respect, and purity.  He feels that the liberal is refusing to evaluate all the considerations, that the liberal is taking a shallow approach to the argument.  He determines that the liberal is taking intellectual shortcuts because he’s more interested in winning points than being honest.  Ultimately, the conservative finds himself believing that liberals are willful idiots and liars.

Now that we know why the other guy is acting and talking the way he does, I hope that we can learn to put ourselves in each other’s shoes and think the way the other person thinks.  It is my hope that by doing so, not only will we find ourselves better able to express ourselves in a way the others can relate to, but we can also be more patient and tolerant when they present their arguments to us.

Crystal Reports duplicate labels

Crystal Reports 2016, SQL

My company has a number of machines.  Some of the machines, when a box is done, need only a single label.  But some machines need two, three, or even four labels for a box.  I’m not… exactly certain why.  I can see two – one for a roll inside the box, and one for the outside of the box.  But we’re not here to argue with the requirements.

Currently my crystal reports are using a SQL query that joins 7 tables from the PlantManager Database.  I can’t modify that database, so I had IT create a new database named DevLink.  I created two tables in DevLink:

DevLink1

This first table is coming from an excel document my manager gave me and that I imported into SQL.

DevLink2

Lastly I create a view that joins these new tables to the existing 7:

CREATE VIEW [TEMKIN\jdittli].[LabelsOff]
AS
select skid.TxnSkidId, skid.SkidCount, skid.Gross, skid.Net, skid.Barcode, skid.TxnDateTime, skid.Container, skid.ContainerDescription,
job.JobNumber, job.[Description],
frm.FormLength, frm.FormWidth, frm.MisTask, frm.uom,
prod.Product, prod.ProductDescription, prod.MisData,
aCount.AutoCountNumber, aCount.AutoCountType, aCount.EquipmentTypeId, aCount.MaxDeliveries, aCount.MaxSpeed, aCount.IsMetric,
employ.Employee, employ.[Description] as Employee_Name,
rQueue.RunQueueNumber, rQueue.RunFormNumber, rQueue.RunFormDescription, rQueue.QuantityToDo, rQueue.NumberUp,
map.Container as NumberLabels,
coun.Number
from plantManager.dbo.TxnSkid skid
inner join plantManager.dbo.job job on job.jobId = skid.JobId
inner join plantManager.dbo.form frm on frm.FormId = skid.FormId
left outer join plantManager.dbo.product prod on prod.JobId = skid.JobId and prod.TaskId = skid.FormId and skid.ProductId = prod.ProductId
left outer join plantManager.dbo.AutoCount aCount on aCount.AutoCountId = skid.AutoCountId
left outer join plantManager.dbo.Employee employ on employ.EmployeeId = skid.EmployeeId
left outer join plantManager.dbo.RunQueue rQueue on skid.runid = rQueue.runid
inner join [DevLink].[TEMKIN\jdittli].[MachineMap] map on map.Machine = acount.AutoCountNumber
inner join [DevLink].[TEMKIN\jdittli].[Counting] coun on coun.Number <= map.Container

Pay special attention to that last line.  The less than or equal means that my view returns multiple rows when the Container field is greater than 1!  After changing my Crystal Reports to pull from the view, then it prints out multiple labels.

Using Crystal Reports Repository

SAP Crystal Reports 2016 Support Pack 3, Crystal Reports Enterprise 2016

It only took a month.  IT had to install visual studio, databases tools, Enterprise tools, repository and a host of other things.  He spent a lot of time on the phone talking to People from India who didn’t know enough to help him.

Alright, so now I have a working login to the repository.  If you remember, I wanted to add my custom functions to the repository so that if I have to change them in the future, I can change them in one place and fix all of my reports.  This part is easy, just go into the formula workshop, right click on the custom function and select ‘Add to Repository‘.  Once that’s complete, I see the functions are listed under ‘Repository Custom Functions‘.  In addition, they’re still listed under ‘Report Custom Functions‘,  where they’ve gained a little cable image to indicate that they’re linked to the Repository.  Cool.

Repository1Repository2

Next, I already have several labels that use the same custom functions (via copy + paste), I want to update all of them to use the repository functions instead.  It took me a bit to figure this out, but it’s actually really simple:  Right click on the Repository function and select ‘Add to Report‘.

Repository3

Crystal reports detects that there’s already a custom report with that name and gives me this popup:

Repository4

And I choose yes.

Alright, so now all of my labels are using the repository function.  What if I have to change the repository function?  Let’s try it out.  I’m going to add a comment to my MisData function.

I’m in a label file called ‘StandardCustomerLabel’.  In the formula workshop, I right click on the Report custom function and select ‘Disconnect from Repository‘.  The little cable goes away, and I can now edit the custom function.  When I’m done, I right click it and select ‘Add to Repository‘.  It asks me if I’m sure and I say yes.  Once again the little cable shows up next to my report custom function.

Now I save and close that label file and go into a second label file named warehousewip.  I pull up the formula workshop and I see that the comment I added is there in the Repository custom function:

Repository5

But unfortunately, it’s NOT visible when I click on the Report custom function.  Well, Huh.  How do I get it to synchronize?  One internet search and I find out that there’s a setting I want to change, which will make it so that when I open a report, it will retrieve any functions from the repository:

https://archive.sap.com/discussions/thread/1496729

Don’t forget to save it if you want the changes to stick.

I’m a little bummed about this.  Eventually I’m going to be using these function in over 150 reports, and every time I change it, I have to re-open all of them and then save them?  It’s a bit of a pain, but at least I don’t have to go into the formula manager, select the formula, and paste for each one.  But what can I do about it?  My application that’s using the labels would have to read the label, determine that the label uses a repository, access the repository and download the latest version of the function and then recompile the label.  It doesn’t have the permission to do any of that, and even if it did, it would be a horrible waste of time if the formula hasn’t changed.  So that’s not an option.  What if Crystal reports updated all the labels automatically?  Well, I’m just saving the labels on my hard drive.  Crystal reports doesn’t know where they are.  Perhaps if I were to check the reports themselves into the repository, then it could.  I think this is possible, but I have no idea how to do it at this time.  If I decide to go that route, it could be the topic of another blog post.

Diving into Crystal Reports

SAP Crystal Reports 2016 Support Pack 3

We acquired some new software and are working on integrating it.  Our old software interfaced with a program called bartender to print labels, but the new program is using Crystal Reports.

To further complicate matters, our company has a large number (~160) of customer specific labels.  Customers like to see their own logos, or maybe they want to hide certain fields… and we’ve been very accommodating.  Some customers use computerized systems to read the labels, meaning that the positioning of certain fields needs to be exact.  Making 160 labels isn’t my job.  That’s actually my boss’s job.  My job is to make the standard label, and make it as easy and fast as possible to take that label and modify it for a customer.

Getting the barcodes to show up correctly has been a bit of a headache.  The default 128 font in Crystal Reports looks nice, but can’t be read by our scanners.  Eventually I found a font on the internet called barcodewiz.  It took me a while to get that to work.  For a while it would only work for one of my test values, but I finally discovered that I needed to change my font size from 24 to 23.  Seriously, who uses font size 23?

Our labels, regardless of customer, all come from the same ~25 fields.  So what I did was create a formula for each field.  About half of the fields are standalone fields in the database, the others are actually stuffed into a single database column that can be parsed as xml, ie:

<AdditionalFields>
      <AdditionalFieldsRow>
            <CustItem>P51C Sea Salt CanadaCA</CustItem>
            <CustPO>2395</CustPO>…

And so I have crystal report parse them out.

Today I started working on a new formula to replicate the quantity string.  You see, based on the sales unit of measure, we might want Feet, Units, or Lbs show up on the label, or any combination of the three.  And so I learned about nested ifs, using formulas in formulas, and a variety of other things.  Here’s my formula, for your reference:

local stringVar SubFieldStart;
local stringVar SubFieldEnd;
local StringVar RightSubstring;
local StringVar SubField;
local StringVar SalesUOM;
local StringVar InventUOM;
local StringVar Combined;
SubField := “SalesUOM”;
SubFieldStart := “<” + SubField + “>”;
SubFieldEnd := “</” + SubField + “>”;
if InStrRev ({Command.MisData}, SubFieldStart) >0 and InStrRev ({Command.MisData}, SubFieldEnd) >0
then
RightSubstring := Right ({Command.MisData}, Length ({Command.MisData}) – InStrRev ({Command.MisData}, SubFieldStart) – length (SubFieldStart) + 1)
else
RightSubstring := “”;
if (RightSubstring <> “”)
then
SalesUOM := Left(RightSubstring, InStrRev(RightSubstring, SubFieldEnd) – 1);
Combined := “Qty: “;
if length(SalesUOM) > 0 then
(
InventUoM := right(SalesUOM, length(SalesUOM) – instrrev(SalesUOM, ‘_’));
SalesUOM := left(SalesUOM, instrrev(SalesUOM, ‘_’) – 1);
//This next part is adapted from Ibis_ShopFlorLabels.getQuantityStr
if (SalesUOM = “”) or (SalesUOM = “lb”) or (SalesUOM = “lbs”) then
(
if (inventUOM = “lbs”) then
Combined := Combined & “LBS: ” & {@lbs} & ” Feet: ” & {@feet}
else
Combined := Combined & “Each: ” & {@each};
);
if (SalesUOM = ‘thou’) or (salesUom = ‘each’) then
Combined := Combined & “Each: ” & {@each};
if (SalesUOM = ‘mbag’) or (salesUom = ‘bag’) then
Combined := Combined & “Bag: ” & {@each};
//mft and msi seem obsolete, but original code displayed these
if (SalesUOM = ‘mft’) or (SalesUOM = ‘msi’) or (salesUom = ‘feet’) then
Combined := Combined & “Feet: ” & {@feet} & ” LBS: ” & {@lbs};
if (SalesUOM = ‘mimp’) then
Combined := Combined & “Imp: ” & {@each} & ” Feet: ” & {@feet};
//Original code displayed roll, but seems silly if it’s always 1
if (SalesUOM = “Roll”) then
(
if (inventUOM = “lbs”) then
Combined := Combined & “LBS: ” & {@lbs} & ” Feet: ” & {@feet}
else
Combined := Combined & “Feet: ” & {@feet} & ” LBS: ” & {@lbs};
);
if (SalesUOM = ‘mtr’) then
Combined := Combined & “Meters: ” & {@feet} * 0.3048 & ” LBS: ” & {@lbs};
//kit, Hour, box, case, packet not supported at this time.
if (Combined = “Qty: “) then
Combined := Combined & “Feet: ” & {@feet} & ” Each: ” & {@Each} & ” LBS: ” & {@lbs};
);
Combined;

I sat back and looked at it.  Job well done, I thought… but that green text, my own comments, were glaring me in the face.  I didn’t have the foggiest how I was going to do some of these units, but it’s almost certain that in the future, this formula would need to change.  I don’t look forward to making the same change to 160 labels!  What was I going to do?

After a lengthy web search gave me nothing, I finally read the application’s help documentation and came across something called a custom function.  Unlike Formulas which query the database, Functions have parameters.  The  first thing I did was create a function to extract a data value from the MISData field (the field with all the xml nodes).  After using this to simplify the standard report, I went into another report and couldn’t find my custom function for it.  Turns out I had to add the custom function to the Repository (which provides source management).

But when I tried to log into the repository, I couldn’t no matter what I tried.  Looks like there is in fact nothing to log into.  You see, the repository is stored in a database and we don’t have Crystal Reports Server installed.  Crystal Reports Server is a separate product, and it costs twice as much as Crystal Reports.  I’m going to have to pitch this to my boss and see what he thinks of it.

The value ‘xxx’ in field ‘FieldName’ is not found in the related table ‘other table name’

So, another developer who did some work, mostly before I arrived.  He added several custom tables.  One of these is the printing table which contains a numerical field named gear, which can be one of several preset values.  I discovered that these preset values are specified by a second table named temColorTableGear.  However, when I run the form to access this second table and try to insert a new record, I get an interesting error:

Invalid field value

“The value ‘39.5’ in field ‘Gear’ is not found in the related table ‘Printing Table’.

It would not let me save the record.

Upon further investigation, I discovered that while the printing table had a relation to the Gear table, there was also a relation heading in the opposite direction (from the temColorTableGear table to the TEMPrintingTable).

ColorGear

The error was essentially saying that I can’t add a new gear because there aren’t any printing table objects that are using that gear number.  When I removed this relation, the error went away, and I was able to create new gears as needed.

 

Writing Excel report based on View

My boss has asked for a report on all the customer correspondence.  This is stored in the axapta database…  I’ll need to create a view that joins several tables, then an excel file that pulls data from the view.

1. Get requirements, background information.  I wanted to know where the object can be accessed through the application, as well as which tables store the relevant data.  I got a list of the fields that he wants on the report, and a general idea of where to find them.

2.  Write a SQL script that joins the necessary tables and gets the fields.

select sActivities.USERMEMO as ‘Comment’, CAST(sActivities.STARTDATETIME AS DATE) as ‘Start Date’,  sActivities.PURPOSE, customer.ACCOUNTNUM as ‘Customer Account’, customerIdentity.NAME as ‘Customer Name’, sActivities.TYPEID as ‘CorrespondenceType’, workerIdentity.NAME as Worker

from ax12live.dbo.SMMACTIVITIES sActivities,  ax12live.dbo.SMMACTIVITYPARENTLINKTABLE ParentLink, ax12live.dbo.CustTable customer, ax12live.dbo.DirPartyTable customerIdentity, ax12live.dbo.HCMWORKER worker, ax12live.dbo.DirPartyTable workerIdentity

where sActivities.USERMEMO != ” and parentlink.PARENTTYPE = 13 and parentlink.RECID = sActivities.RECID and ParentLink.REFRECID = customer.RECID and customerIdentity.RECID = customer.PARTY and worker.RECID = sActivities.RESPONSIBLEWORKER and worker.PERSON = workerIdentity.RECID

3.  Create the view.  The fastest way to do this is take an existing view, Right click and go to create to new query window.  Then modify the script by changing the name and replace the SQL component with what you wrote in step 2.  Resolve any SQL errors you get – view sql sometimes has additional rules.

4.  Go to My Document -> My Data sources and create a new data connection file (extension .odc).  Again, the easiest way to do this is to copy an existing one.  Give it the name of your current view.  If you don’t have a data connection file, here’s one to work with:

<html xmlns:o=”urn:schemas-microsoft-com:office:office”
xmlns=”http://www.w3.org/TR/REC-html40″&gt;

<head>
<meta http-equiv=Content-Type content=”text/x-ms-odc; charset=utf-8″>
<meta name=ProgId content=ODC.Table>
<meta name=SourceType content=OLEDB>
<meta name=Catalog content=axBA>
<meta name=Schema content=”TEMKIN\jdittli”>
<meta name=Table content=Dalan-CollectionsReport>
<title>naxsql axBA Dalan-CollectionsReport</title>
<xml id=docprops><o:DocumentProperties
xmlns:o=”urn:schemas-microsoft-com:office:office”
xmlns=”http://www.w3.org/TR/REC-html40″&gt;
<o:Name>naxsql axBA Dalan-CollectionsReport</o:Name>
</o:DocumentProperties>
</xml><xml id=msodc><odc:OfficeDataConnection
xmlns:odc=”urn:schemas-microsoft-com:office:odc”
xmlns=”http://www.w3.org/TR/REC-html40″&gt;
<odc:Connection odc:Type=”OLEDB”>
<odc:ConnectionString>Provider=SQLOLEDB.1;Integrated Security=SSPI;Persist Security Info=True;Data Source=naxsql;Use Procedure for Prepare=1;Auto Translate=True;Packet Size=4096;Workstation ID=BADAMS;Use Encryption for Data=False;Tag with column collation when possible=False;Initial Catalog=axBA</odc:ConnectionString>
<odc:CommandType>Table</odc:CommandType>
<odc:CommandText>&quot;axBA&quot;.&quot;TEMKIN\jdittli&quot;.&quot;Dalan-CollectionsReport&quot;</odc:CommandText>
</odc:Connection>
</odc:OfficeDataConnection>
</xml>
<style>
<!–
.ODCDataSource
{
behavior: url(dataconn.htc);
}
–>
</style>

</head>

<body onload=’init()’ scroll=no leftmargin=0 topmargin=0 rightmargin=0 style=’border: 0px’>
<table style=’border: solid 1px threedface; height: 100%; width: 100%’ cellpadding=0 cellspacing=0 width=’100%’>
<tr>
<td id=tdName style=’font-family:arial; font-size:medium; padding: 3px; background-color: threedface’>
&nbsp;
</td>
<td id=tdTableDropdown style=’padding: 3px; background-color: threedface; vertical-align: top; padding-bottom: 3px’>

&nbsp;
</td>
</tr>
<tr>
<td id=tdDesc colspan=’2′ style=’border-bottom: 1px threedshadow solid; font-family: Arial; font-size: 1pt; padding: 2px; background-color: threedface’>

&nbsp;
</td>
</tr>
<tr>
<td colspan=’2′ style=’height: 100%; padding-bottom: 4px; border-top: 1px threedhighlight solid;’>

 

</td>
</tr>
</table>

function init() {
var sName, sDescription;
var i, j;

try {
sName = unescape(location.href)

i = sName.lastIndexOf(“.”)
if (i>=0) { sName = sName.substring(1, i); }

i = sName.lastIndexOf(“/”)
if (i>=0) { sName = sName.substring(i+1, sName.length); }

document.title = sName;
document.getElementById(“tdName”).innerText = sName;

sDescription = document.getElementById(“docprops”).innerHTML;

i = sDescription.indexOf(“escription>”)
if (i>=0) { j = sDescription.indexOf(“escription>”, i + 11); }

if (i>=0 && j >= 0) {
j = sDescription.lastIndexOf(“

if (j>=0) {
sDescription = sDescription.substring(i+11, j);
if (sDescription != “”) {
document.getElementById(“tdDesc”).style.fontSize=”x-small”;
document.getElementById(“tdDesc”).innerHTML = sDescription;
}
}
}
}
catch(e) {

}
}

</body>

</html>

5.  Create a new Excel file.  Go to Data tab -> Connections.  Click Add, and find the file you created in step 4.  Then go to Data tab -> Existing Connections, and double click on your connection.  Accept the default.  The data comes into your excel file.  If you have everything you need, you can stop here.

6.  Name your sheet ‘RawData’ and create a new one.  Using index+match, sumifs, sumproduct, and any other excel functions you desire, massage the data to get the sums and averages and history for your report.

7.  Turns out that my SQL wasn’t doing the join correctly.  After I got the right sql, I updated the view by right clicking it, choosing design, and copying the correct SQL in there.  Then in Excel, all you have to do is just hit refresh-all.

On Debugging

I consider the ability to identify bugs the most important skill a developer can have.  I certainly spend more time chasing down bugs than any other activity.

1. The first step of debugging is identify your test case.  Get specifics and write them down in a notepad (not a sticky note that you’ll loose!).  If someone comes to you and says ‘I can’t release orders’, ask them for a specific order that they cannot release.  Then, on your machine, find that order and try to release it.  If you get the same error as the user, then you have your test case.

You test case needs to be repeatable and fast.  If your importing a file or running a batch that takes a fair amount of time, that is not a proper test case.  The proper test case would be a modified file that only contains one row, or a script that executes your batch for a single record.

2. Take some time to understand what the program is doing.  Sometimes, what a user identifies as a bug is not a bug at all.  Think about what the program is doing and why.  If you don’t know what the program is doing, then step through the code to find out.  Look at the data objects that the program is using.  Get up and walk around.  I can’t tell you how many bugs I’ve make breakthroughs on while going to the bathroom or pacing around the office.

This is where software language makes a huge difference.  My first company out of college had a very archaic language called cache that was very difficult to read, and used primitive structures (it was not object based).  While you could step through code, doing so was difficult and tedious.  Finding the values of objects and variables while you were debugging was difficult.  Lastly, it was nearly impossible to execute a workflow and identify which lines of code were being executed by that workflow.

My second company used a proprietary language called Gold which was very strong in this department.  It was object oriented, and read much like java or C#.  In the application, you could right click and bring up a menu with several options, among witch were ‘debug object’, ‘debug next ui call’ and ‘debug ui agent’.  The first gave you a view of all the variables in the current object, with the ability to snake through to see any object it referenced.  The second gave you the ability to determine exactly what code lay behind any button or field.  Finally, ‘Debug UI Agent’ allowed me to look at the composition of the screen or display window.

My current company uses Microsoft Dynamics, which has a proprietary language called X++.  It’s a weakly object oriented language, and it doesn’t have some of the abilities I enjoyed with Gold.  I’m able to get along though… the code is readable and the debugging abilities are vastly superior to cache.

3. Once you truly understand what the program is doing, it’s usually easy to fix.  However, usually you’ll first arrive at a position where you think you know what its doing, but you aren’t entirely sure.  Then you try to change what you think is wrong and see how that affects things.  If you’re wrong, you identify something else and try that.  By the time you’ve fixed the bug, you generally should be able to explain exactly what went wrong.  Without a proper test case (step 1), step 3 is impossible.

4.  Document your fix.  If necessary, cleanup your code and then retest it.  Put comments in the code, writeup a code change document, and/or create a regression test for it.

5.  Make sure your fix gets committed to live properly.  This is where source control comes in.  If the developer down the hall has fixed some other bug, and you revert his fix to insert yours, then he’s going to be mad at you!  You should test that your fix still works after your code has been moved into Test or Live.

6.  Clean up production data, if necessary.  If your error was in production and resulted in wrong values being saved to the database, then you need to identify the bad records and a script or other process to update them.  Don’t be tempted to do this step out of order – you need to first stop the bleeding before you tend to the wound.

On College

Hello,

So, a while ago a co-worker asked me how important my college experience is to my career.  I thought I’d elaborate a bit on this.

  1. College is, to date, the easiest way to demonstrate that you have certain attributes an employer might find useful.  The will and ability to learn new things is very important in a job where you’re probably going to be learning a new software language, and many other new things.  College also requires a certain stick-to-it-ness… employers ideally want you to be willing to stick around a few years.
  2. There is a vast amount of knowledge that you will learn in College, and can safely forget until you need it.  There are a lot of algorithms and operating system and electrical engineering information that I simply haven’t had to use on the job.  However, I’m certain that others in my graduating class have used these things.  Furthermore, it’s entirely possible that I will use some of these things eventually, either in my current job or another.  Researching something that you’ve forgotten is typically a quick google or wikipedia thing, but when you don’t know that you’re ignorant, overcoming that knowledge gap is much, much harder.
  3. College never went very deep in software development.  I never did a data migration, or worked with a database mapping for an object oriented project, or created a report.  Perhaps those would have been included in Grad school.
  4. College really doesn’t teach you about the type of careers out there, and what kinds of career paths are better than others.

In Summary, college gives you a general base of knowledge and skill so that you will be useful for any job.  And after you quit that job, you will have the skills to learn what you need to know at the next one.  It doesn’t give you very deep skills in a particular job though.  Those come from job experience.

Can you skip college and go right into programming without a computer science degree?  Yes, many people do this.  Some transition from another job into a programming job (ie, within the same company).  Others teach themselves how to do web development or app development and simply start working at age 18 (or they get an associates and start at age 20).  There’s also the people who start their own business in their parent’s garage and succeed marvelously… but those are rare, and most of them had some college before they dropped out anyway (Bill Gates, I’m looking at you!).

The problem with all these shortcuts is that you might get that first job, but it’s going to be really hard to transition jobs thereafter.  You might be fine if you can switch to another job using the exact same technology as your first, but that isn’t always an option.  Employers looking at a resume want to see that you have relevant experience and if you don’t have that, you’d better have some way to demonstrate that you have general knowledge.  There are many ways to do this, and a college degree may not always be the best, but it generally is the easiest and simplest.

Illegal data conversion from original field

Using Microsoft Dynamics (Axapta) 2012 R1.

So yesterday I added a field named “QuotationNumAcross” to the salesLine table.  Today, I show it to my manager and he say ‘Oh, we didn’t want it there, we want it on the sales quotation table.  So I deleted the field.  When I import it into live the import fails on the synchronization step with two errors like this:

Illegal data conversion from original field SalesLine.<field name> to SalesLine.<some other field>: unable to convert data types to anything but character field type (0 to 4)

I went to google and started looking around… and then Live suddenly crashed.  For everyone.  That was odd… I informed our IT guy and came back.  The service hadn’t restarted so I started it.  It lasted 5 minutes before crashing again.  At this point, I realized that I wasn’t going to be having a good day today.

The internet recommended that I rename my table, synchronize to get the original table, and then copy the data from the renamed table to the original.  I had to rename the indexes as well, but when it came time to copy the data I got sql this error:

Msg 257, Level 16, State 3, Line 1
Implicit conversion from data type datetime to numeric is not allowed. Use the CONVERT function to run this query.

I didn’t know how to fix that, nor did I want to spend the time to find out.  Live was down, and it was my fault, and I needed it back up ASAP.  Instead, I deleted the ‘QuotationNumAcross’ field directly from the table, and renamed it back to its original name.  Then in Axapta I confirmed I could synchronize and everything was fine.

Except not quite.  I soon get calls saying that the sales confirmation report is displaying the fields wrong – the packing spec was showing up where the UPC is supposed to be, and so forth.  Restarting the reporting service didn’t fix it, but it did get fixed when I resaved and recompiled the SalesConfirmDP class.  A little later I get a call that they can’t create sales orders, and I fixed that one by recompiling SalesLineType.  I wouldn’t be surprised if I get more errors, the day is still young.

So, this is how you solve that problem the hard way.  The solution was complicated because it occurred in live, and I didn’t have time to conduct a thorough investigation.  If anyone know what I should have done instead, leave a reply below.

Batch execution tips

Microsoft Dynamics (Axapta) 2012 R1.

So, I spent a chunk of time this week and last on an interface that runs in batch mode.  If I ran outside of batch mode, it worked fine, but in batch mode, it failed.  Here are some things that might help others in this situation.

  1. You cannot call the evalbuf method in batch mode.  Don’t ask me why, but it simply is not an option.  Now, not a lot of places use this, but a big one is the UnitOfMeasureConverter class.  I had to convert from lbs to feet, from feet to meters, inches to mm and vice versa.  Most of the factors you’ll need can be found on the internet.
  2. If you use the num2str method, don’t put a -1 in any of the parameter fields.  When in batchmode, Ax can’t interpret the -1.
  3. If you have code that might be executed in batch mode but also might be executed by your user directly, you can make your batch class entends RunBaseBatch which allows you to call this.isInBatch() to determine if you’re running in batch mode.
  4. Permissions to write a file are tricky when in batch mode.  See the following code:
    if (this.isInBatch())
    {
        output = new TextIo(@filepath, #IO_WRITE);
        output.write(messageBuffer);
    }
    else
    {
        this.EnhancedPermissionWrite(_tableObject);
    }

    private void EnhancedPermissionWrite(common _tableObject = null)
    {
        #File
        Set permissionSet;
        System.Exception interopException;
        Filename destinationFileName = ‘\\\\autoct\\Plant Manager Connector\\MIS_In\\’ +         TabID + int2str(TableObject.recid) + ‘.xml’;
        try
        {
            permissionSet = new Set(Types::Class);
            permissionSet.add(new FileIOPermission(destinationFileName,#io_write));
            permissionSet.add(new InteropPermission(InteropKind::ClrInterop));
            CodeAccessPermission::assertMultiple(permissionSet);
            System.IO.File::WriteAllText(destinationFileName, messageBuffer);
            CodeAccessPermission::revertAssert();
        }
        catch(Exception::CLRError)
  5. Because I couldn’t step through the code when executing as a batch, I did by debugging by writing output to a file.  This is the bad way to do this: 

    textIo.write(“Just wrote a ” + tabid + ‘Command message with ‘ + int2str(strLen(messageBuffer)) + ‘ characters to ‘ + FilePath);

    You see, when I export my batch to live, I won’t want it writing out to a file, because file i/o is one of the most taxing performance hogs, and it will considerably slow down my batch.  It’s great for testing, but after I’m done, it will be a pain to go through and remove all of these statements.  A better way is to create a method to write to the file:private void trace(str message)
    {
        if (textIo)
        {
            textIo.write(message);
        }
    }

    Then you can call it like this: 

    this.trace(“Just wrote a ” + tabid + ‘Command message with ‘ + int2str(strLen(messageBuffer)) + ‘ characters to ‘ + FilePath);

    This allows me to easily turn tracing off when I’m done debugging, simply by commenting out a few lines of code in a single place.  If I later have difficulty with the batch, I can quickly and easily turn all that tracing back on again.