First Article via the Inspection Workbench

I’m wondering if anyone has come across a similar requirement and potentially developed a solution.

First articles out of the box in E10 doesn’t work in our environment - for us a first article is typically a series of quality checks at key stages and only on a certain occasion (first time we have ran in production, follow on from customer complaint etc). Our quality team have found a reasonably slick method of managing this by manually adding first articles to their workbench as production orders are released - ie add first article to workbench for op 10 on job 123456.

This works well for fairly basic inspections/assemblies but as each first article inspection is added individually by choosing the job, assembly and operation it is becoming an onerous task where we are doing multiple inspections on multiple ops/assemblies within the same job or in some cases every operation on a multi assembly/multi operation job.

Is there any straightforward way to bulk add first article inspections to the inspection for example for the whole job or a range of operations - I’m guessing this would need to be an updateable dashboard so if anyone has done something similar can they give me some pointers or how best to go about this.

I can’t see a suitable template in the DMT but again if someone has done this using the DMT then please do let me know.

This is a great idea! I was just processing through how we are going to handle FAI’s in our shop. We are in the transition of moving to epicor, so I am playing around with different customization’s. Let me try to wip one up, and see if I can get it to work. Essentially what I am going to try to create is a custom form/Button where you enter the fai qty, range of operations, and it will automatically update the qty’s on the MOM. This what you are looking for?

You could add an inspection plan to each Operation. It can be skipped over if there is no FAI.

You could also edit the FAI before the job is released. Maybe make QA be the one to release jobs so they can edit as needed?

1 Like

Nick,

This is close to what we would want but we would want MOM for specific jobs updated not the part MOM. Ideally put the job number in and a list of all assemblies/ops on the jobs is returned. You can either select them or all or choose selected ones - in our case the quantity is irrelevant, op needs inspected or not inspected so same quantity would apply to all ops requiring inspection. If you can knock something up that would be great.

James,

So I was able to create a form that works. You enter the Job#, the assemblies load, you check the
box next to the ones you want to add FAI to, and click a button. It will then go and add a FAI qty of 1 to each operation under that assembly for that job. Is this what you were looking for? If so, I am trying to think of the best way that I can get that to you. I can potentially email you the exported customization, then have you load it in? we will have to figure that out.

Nick that would ideal - not sure how to message you with my email address?

James, I need to figure out how I can do this for you. Ideally I want you to be able to import the customization, and not have to do anything. Let me play around with it. Do you want this to be a form that is on the jobentry form? be a separate form all together?

Nick,

Whichever is easier.

Looking like it will be a little difficult to get it to you, but what I can do is give you the code, and you can do what you want with it. Going to take a little work on your end to fit it to your program, but the bulk of the code is there.

That would be great

Here is the example I created to see if I could get it to work. You enter the job#, and hit tab. It will then load all assemblies. You check the box next to the ones you want to add an FAI quantity to, and it will add an FAI qty of 1 to each operation with in that assembly.

This is my code below for the above form. You would have to create the form (Or use the code I provided as a guideline). In order to get this to work, you will have to load the jobentry adapter. you do this by going to tools>Assembly Reference Manager>Add Custom Reference then change the file type to all, and select “Erp.Adapters.JobEntry”,“Erp.Contracts.BO.JobEntry”,“ERP.UI.JobEntry”

I hope this helps. I know it is not as ideal as handing you a txt file, but, if you learn how to do this, and what it means, you can create a whole lot of customizations!


// Custom code for MainController
// Created: 5/24/2017 7:59:25 AM
// **************************************************
using System;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Windows.Forms;
using Ice.BO;
using Ice.UI;
using Ice.Lib;
using Ice.Adapters;
using Erp.Adapters;
using Ice.Lib.Customization;
using Ice.Lib.ExtendedProps;
using Ice.Lib.Framework;
using Ice.Lib.Searches;
using Ice.UI.FormFunctions;

public class Script
{
	// ** Wizard Insert Location - Do Not Remove 'Begin/End Wizard Added Module Level Variables' Comments! **
	// Begin Wizard Added Module Level Variables **

	// End Wizard Added Module Level Variables **

	// Add Custom Module Level Variables Here **

	public void InitializeCustomCode()
	{
		// ** Wizard Insert Location - Do not delete 'Begin/End Wizard Added Variable Initialization' lines **
		// Begin Wizard Added Variable Initialization

		// End Wizard Added Variable Initialization

		// Begin Wizard Added Custom Method Calls

		this.epiTextBoxC2.AfterExitEditMode += new System.EventHandler(this.epiTextBoxC2_AfterExitEditMode);
		this.epiButtonC1.Click += new System.EventHandler(this.epiButtonC1_Click);
		// End Wizard Added Custom Method Calls
	}

	public void DestroyCustomCode()
	{
		// ** Wizard Insert Location - Do not delete 'Begin/End Wizard Added Object Disposal' lines **
		// Begin Wizard Added Object Disposal

		this.epiTextBoxC2.AfterExitEditMode -= new System.EventHandler(this.epiTextBoxC2_AfterExitEditMode);
		this.epiButtonC1.Click -= new System.EventHandler(this.epiButtonC1_Click);
		// End Wizard Added Object Disposal

		// Begin Custom Code Disposal

		// End Custom Code Disposal
	}

//This is what updates the assemblies after the job# is entered into the text box.
	private void epiTextBoxC2_AfterExitEditMode(object sender, System.EventArgs args)
	{
		try
		{
			//Calls the job entry adapter
			JobEntryAdapter adapterJobEntry = new JobEntryAdapter(this.oTrans);
			adapterJobEntry.BOConnect();

			//This is using the Job# entered in the textbox as a reference to find the record.
			adapterJobEntry.GetByID(this.epiTextBoxC2.Text);

			//This is creating a view based off of the record that we found above.
			EpiDataView edvPA = new EpiDataView(); 
			edvPA.dataView = new DataView(adapterJobEntry.JobEntryData.JobAsmbl);
		
			//This is initiating the variable for the loop that takes place below.
			int i = 0;
		
			//This is finding out how many assemblies there are attached to the job that we entered.
			int x = adapterJobEntry.JobEntryData.JobAsmbl.Rows.Count;
		
			//This is enabling, or disabling the check boxes based off of how many assemblies there are for the job.
			if (x < 1)
			{
				this.epiTextBox1.Text = "";
				this.epiCheckBoxC1.Enabled = false;
				this.epiCheckBoxC1.Checked = false;
				MessageBox.Show("This Job does not have any assemblies, please add a method of manufacturing before using this feature.");
				
			}
			else
			{
				this.epiTextBox1.Text = "";
				this.epiCheckBoxC1.Enabled = true;
				this.epiCheckBoxC1.Checked = false;
			}

			if (x < 2)
			{
				this.epiTextBox2.Text = "";
				this.epiCheckBox1.Enabled = false;
				this.epiCheckBox1.Checked = false;
			}
			else
			{
				this.epiTextBox2.Text = "";
				this.epiCheckBox1.Enabled = true;
				this.epiCheckBox1.Checked = false;
			}

			if (x < 3)
			{
				this.epiTextBox3.Text = "";
				this.epiCheckBox2.Enabled = false;
				this.epiCheckBox2.Checked = false;
			}
			else
			{
				this.epiTextBox3.Text = "";
				this.epiCheckBox2.Enabled = true;
				this.epiCheckBox2.Checked = false;
			}

			if (x < 4)
			{
				this.epiTextBox4.Text = "";
				this.epiCheckBox4.Enabled = false;
				this.epiCheckBox4.Checked = false;
			}
			else
			{
				this.epiTextBox4.Text = "";
				this.epiCheckBox4.Enabled = true;
				this.epiCheckBox4.Checked = false;
			}

			if (x < 5)
			{
				this.epiTextBox5.Text = "";
				this.epiCheckBox6.Enabled = false;
				this.epiCheckBox6.Checked = false;
			}
			else
			{
				this.epiTextBox5.Text = "";
				this.epiCheckBox6.Enabled = true;
				this.epiCheckBox6.Checked = false;
			}

			if (x < 6)
			{
				this.epiTextBox6.Text = "";
				this.epiCheckBox8.Enabled = false;
				this.epiCheckBox8.Checked = false;
			}
			else
			{
				this.epiTextBox6.Text = "";
				this.epiCheckBox8.Enabled = true;
				this.epiCheckBox8.Checked = false;
			}

			if (x < 7)
			{
				this.epiTextBox7.Text = "";
				this.epiCheckBox10.Enabled = false;
				this.epiCheckBox10.Checked = false;
			}
			else
			{
				this.epiTextBox7.Text = "";
				this.epiCheckBox10.Enabled = true;
				this.epiCheckBox10.Checked = false;
			}

			if (x < 8)
			{
				this.epiTextBox8.Text = "";
				this.epiCheckBox12.Enabled = false;
				this.epiCheckBox12.Checked = false;
			}
			else
			{
				this.epiTextBox8.Text = "";
				this.epiCheckBox12.Enabled = true;
				this.epiCheckBox12.Checked = false;
			}


			//This loop is what fills in the text boxes with the assembly data.
			while (i < x)
			{	
				//This pulls in the assembly and the part# for the currently selected row of data.
				string asm = Convert.ToString(edvPA.dataView[edvPA.Row+i]["AssemblySeq"]);
				string part = Convert.ToString(edvPA.dataView[edvPA.Row+i]["PartNum"]);

				//These fill the textboxes with the data.
				if (i == 0)
				{
					this.epiTextBox1.Text = asm + "-" + part;
				}
				else if (i==1)
				{
					this.epiTextBox2.Text = asm + "-" + part;
				}	
				else if (i==2)
				{
					this.epiTextBox3.Text = asm + "-" + part;
				}
				else if (i==3)
				{
					this.epiTextBox4.Text = asm + "-" + part;
				}
				if (i==4)
				{
					this.epiTextBox5.Text = asm + "-" + part;
				}
				else if (i==5)
				{
					this.epiTextBox6.Text = asm + "-" + part;
				}
				else if (i==6)
				{
					this.epiTextBox7.Text = asm + "-" + part;
				}
				else if (i==7)
				{
					this.epiTextBox8.Text = asm + "-" + part;
				}

			i = i+1;
			}
			adapterJobEntry.Dispose();
	
		//Error Handling
		} catch (System.Exception ex)
		{
			ExceptionBox.Show(ex);
		}

	}

	//This is what sets up the form so that everything is empty when it is loaded.
	private void MainController_Load(object sender, EventArgs args)
	{
		this.epiCheckBoxC1.Checked = false;
		this.epiCheckBox1.Checked = false;
		this.epiCheckBox2.Checked = false;
		this.epiCheckBox4.Checked = false;
		this.epiCheckBox6.Checked = false;
		this.epiCheckBox8.Checked = false;
		this.epiCheckBox10.Checked = false;
		this.epiCheckBox12.Checked = false;

		this.epiTextBox1.Text = "";
		this.epiTextBox2.Text = "";
		this.epiTextBox3.Text = "";
		this.epiTextBox4.Text = "";
		this.epiTextBox5.Text = "";
		this.epiTextBox6.Text = "";
		this.epiTextBox7.Text = "";
		this.epiTextBox8.Text = "";

		this.epiTextBox1.Value = "";
		this.epiTextBox2.Value = "";
		this.epiTextBox3.Value = "";
		this.epiTextBox4.Value = "";
		this.epiTextBox5.Value = "";
		this.epiTextBox6.Value = "";
		this.epiTextBox7.Value = "";
		this.epiTextBox8.Value = "";

		this.epiCheckBoxC1.Enabled = false;
		this.epiCheckBox1.Enabled = false;
		this.epiCheckBox2.Enabled = false;
		this.epiCheckBox4.Enabled = false;
		this.epiCheckBox6.Enabled = false;
		this.epiCheckBox8.Enabled = false;
		this.epiCheckBox10.Enabled = false;
		this.epiCheckBox12.Enabled = false;

		this.epiTextBox1.ReadOnly = true;
		this.epiTextBox2.ReadOnly = true;
		this.epiTextBox3.ReadOnly = true;
		this.epiTextBox4.ReadOnly = true;
		this.epiTextBox5.ReadOnly = true;
		this.epiTextBox6.ReadOnly = true;
		this.epiTextBox7.ReadOnly = true;
		this.epiTextBox8.ReadOnly = true;

		this.epiTextBoxC2.Text = "";
		this.epiTextBoxC2.Value = "";
	}

	//This is the code for the button
	private void epiButtonC1_Click(object sender, System.EventArgs args)
	{
		try
		{
			//This checks to see if a Job has been entered, if not, it will give an error.
			if (this.epiTextBoxC2.Text == "")
			{
				MessageBox.Show("Please enter a Job#.");
				return;
			}
		
			//This checks to see if any of the check boxes are checked true when the button is clicked. If not, it will give an error.
			if (this.epiCheckBoxC1.Checked)
			{

			}
			else
			{
				if (this.epiCheckBox1.Checked)
				{
	
				}
				else
				{
					if (this.epiCheckBox2.Checked)
					{
		
					}
					else
					{
						if (this.epiCheckBox4.Checked)
						{
			
						}
						else
						{
							if (this.epiCheckBox6.Checked)
							{
				
							}
							else
							{
								if (this.epiCheckBox8.Checked)
								{
					
								}
								else
								{
									if (this.epiCheckBox10.Checked)
									{
						
									}
									else
									{
										if (this.epiCheckBox12.Checked)
										{
							
										}
										else
										{
											MessageBox.Show("Please select an assembly to add FAI Quantities to.");
											return;
										}
									}
								}
							}
						}
					}
				}
			}

			//Loads the jobentry adapter
			JobEntryAdapter adapterJobEntry = new JobEntryAdapter(this.oTrans);
			adapterJobEntry.BOConnect();

			//Finds our record based off of the job# we entered
			adapterJobEntry.GetByID(this.epiTextBoxC2.Text);
			
			//Initializes our variables
			string[] separators = {"-"};
			int x;
			int i;

			//This runs through every checkbox, if it is checked, then it will add an FAI qty to the operation.
			if (this.epiCheckBoxC1.Checked)
			{
				//Finds the assembly that is currently selected
				string text = this.epiTextBox1.Text;
				string[] asm1text = text.Split(separators, StringSplitOptions.RemoveEmptyEntries);

				string asm1 = asm1text[0];

				//loads in the operations for the current assembly
				adapterJobEntry.GetDatasetForTree(this.epiTextBoxC2.Text, Convert.ToInt32(asm1), Convert.ToInt32(asm1), true);

				EpiDataView edvPA1 = new EpiDataView(); 
				edvPA1.dataView = new DataView(adapterJobEntry.JobEntryData.JobOper);
				
				//counts how many operations there are for the current assembly
				i = 0;
				x = adapterJobEntry.JobEntryData.JobOper.Rows.Count;

				//Loops through all operations and changes the fai quantity to 1.
				while (i<x)
				{
					DataRow editRow = adapterJobEntry.JobEntryData.JobOper.Rows[i]; 
					editRow.BeginEdit(); 
					editRow["FAQty"] = 1; 
					editRow.EndEdit(); 
					adapterJobEntry.Update(); 
			
					i=i+1;
				}

			}

			if (this.epiCheckBox1.Checked)
			{
				string text = this.epiTextBox2.Text;
				string[] asm2text = text.Split(separators, StringSplitOptions.RemoveEmptyEntries);

				string asm2 = asm2text[0];
				adapterJobEntry.GetDatasetForTree(this.epiTextBoxC2.Text, Convert.ToInt32(asm2), Convert.ToInt32(asm2), true);

				EpiDataView edvPA2 = new EpiDataView(); 
				edvPA2.dataView = new DataView(adapterJobEntry.JobEntryData.JobOper);
				
				i = 0;
				x = adapterJobEntry.JobEntryData.JobOper.Rows.Count;

				while (i<x)
				{
					DataRow editRow = adapterJobEntry.JobEntryData.JobOper.Rows[i]; 
					editRow.BeginEdit(); 
					editRow["FAQty"] = 1; 
					editRow.EndEdit(); 
					adapterJobEntry.Update(); 
			
					i=i+1;
				}
			}

			if (this.epiCheckBox2.Checked)
			{
				string text = this.epiTextBox3.Text;
				string[] asm3text = text.Split(separators, StringSplitOptions.RemoveEmptyEntries);

				string asm3 = asm3text[0];
				adapterJobEntry.GetDatasetForTree(this.epiTextBoxC2.Text, Convert.ToInt32(asm3), Convert.ToInt32(asm3), true);

				EpiDataView edvPA3 = new EpiDataView(); 
				edvPA3.dataView = new DataView(adapterJobEntry.JobEntryData.JobOper);
				
				i = 0;
				x = adapterJobEntry.JobEntryData.JobOper.Rows.Count;

				while (i<x)
				{
					DataRow editRow = adapterJobEntry.JobEntryData.JobOper.Rows[i]; 
					editRow.BeginEdit(); 
					editRow["FAQty"] = 1; 
					editRow.EndEdit(); 
					adapterJobEntry.Update(); 
			
					i=i+1;
				}
			}
		
			if (this.epiCheckBox4.Checked)
			{
				string text = this.epiTextBox4.Text;
				string[] asm4text = text.Split(separators, StringSplitOptions.RemoveEmptyEntries);

				string asm4 = asm4text[0];
				adapterJobEntry.GetDatasetForTree(this.epiTextBoxC2.Text, Convert.ToInt32(asm4), Convert.ToInt32(asm4), true);

				EpiDataView edvPA4 = new EpiDataView(); 
				edvPA4.dataView = new DataView(adapterJobEntry.JobEntryData.JobOper);
				
				i = 0;
				x = adapterJobEntry.JobEntryData.JobOper.Rows.Count;

				while (i<x)
				{
					DataRow editRow = adapterJobEntry.JobEntryData.JobOper.Rows[i]; 
					editRow.BeginEdit(); 
					editRow["FAQty"] = 1; 
					editRow.EndEdit(); 
					adapterJobEntry.Update(); 
			
					i=i+1;
				}
			}

			if (this.epiCheckBox6.Checked)
			{
				string text = this.epiTextBox5.Text;
				string[] asm5text = text.Split(separators, StringSplitOptions.RemoveEmptyEntries);

				string asm5 = asm5text[0];
				adapterJobEntry.GetDatasetForTree(this.epiTextBoxC2.Text, Convert.ToInt32(asm5), Convert.ToInt32(asm5), true);

				EpiDataView edvPA5 = new EpiDataView(); 
				edvPA5.dataView = new DataView(adapterJobEntry.JobEntryData.JobOper);
				
				i = 0;
				x = adapterJobEntry.JobEntryData.JobOper.Rows.Count;

				while (i<x)
				{
					DataRow editRow = adapterJobEntry.JobEntryData.JobOper.Rows[i]; 
					editRow.BeginEdit(); 
					editRow["FAQty"] = 1; 
					editRow.EndEdit(); 
					adapterJobEntry.Update(); 
			
					i=i+1;
				}
			}

			if (this.epiCheckBox8.Checked)
			{
				string text = this.epiTextBox6.Text;
				string[] asm6text = text.Split(separators, StringSplitOptions.RemoveEmptyEntries);

				string asm6 = asm6text[0];
				adapterJobEntry.GetDatasetForTree(this.epiTextBoxC2.Text, Convert.ToInt32(asm6), Convert.ToInt32(asm6), true);

				EpiDataView edvPA6 = new EpiDataView(); 
				edvPA6.dataView = new DataView(adapterJobEntry.JobEntryData.JobOper);
				
				i = 0;
				x = adapterJobEntry.JobEntryData.JobOper.Rows.Count;

				while (i<x)
				{
					DataRow editRow = adapterJobEntry.JobEntryData.JobOper.Rows[i]; 
					editRow.BeginEdit(); 
					editRow["FAQty"] = 1; 
					editRow.EndEdit(); 
					adapterJobEntry.Update(); 
			
					i=i+1;
				}
			}

			if (this.epiCheckBox10.Checked)
			{
				string text = this.epiTextBox7.Text;
				string[] asm7text = text.Split(separators, StringSplitOptions.RemoveEmptyEntries);

				string asm7 = asm7text[0];
				adapterJobEntry.GetDatasetForTree(this.epiTextBoxC2.Text, Convert.ToInt32(asm7), Convert.ToInt32(asm7), true);

				EpiDataView edvPA7 = new EpiDataView(); 
				edvPA7.dataView = new DataView(adapterJobEntry.JobEntryData.JobOper);
				
				i = 0;
				x = adapterJobEntry.JobEntryData.JobOper.Rows.Count;

				while (i<x)
				{
					DataRow editRow = adapterJobEntry.JobEntryData.JobOper.Rows[i]; 
					editRow.BeginEdit(); 
					editRow["FAQty"] = 1; 
					editRow.EndEdit(); 
					adapterJobEntry.Update(); 
			
					i=i+1;
				}
			}

			if (this.epiCheckBox12.Checked)
			{
				string text = this.epiTextBox8.Text;
				string[] asm8text = text.Split(separators, StringSplitOptions.RemoveEmptyEntries);

				string asm8 = asm8text[0];
				adapterJobEntry.GetDatasetForTree(this.epiTextBoxC2.Text, Convert.ToInt32(asm8), Convert.ToInt32(asm8), true);

				EpiDataView edvPA8 = new EpiDataView(); 
				edvPA8.dataView = new DataView(adapterJobEntry.JobEntryData.JobOper);
				
				i = 0;
				x = adapterJobEntry.JobEntryData.JobOper.Rows.Count;

				while (i<x)
				{
					DataRow editRow = adapterJobEntry.JobEntryData.JobOper.Rows[i]; 
					editRow.BeginEdit(); 
					editRow["FAQty"] = 1; 
					editRow.EndEdit(); 
					adapterJobEntry.Update(); 
			
					i=i+1;
				}
			}
		
		//Tells you it has been completed, and it clears the form
		MessageBox.Show("FAI Quantities have been added!");
		load();
		} catch (System.Exception ex)
		{
			ExceptionBox.Show(ex);
		}
	}

	
	private void load()
	{
		this.epiCheckBoxC1.Checked = false;
		this.epiCheckBox1.Checked = false;
		this.epiCheckBox2.Checked = false;
		this.epiCheckBox4.Checked = false;
		this.epiCheckBox6.Checked = false;
		this.epiCheckBox8.Checked = false;
		this.epiCheckBox10.Checked = false;
		this.epiCheckBox12.Checked = false;

		this.epiTextBox1.Text = "";
		this.epiTextBox2.Text = "";
		this.epiTextBox3.Text = "";
		this.epiTextBox4.Text = "";
		this.epiTextBox5.Text = "";
		this.epiTextBox6.Text = "";
		this.epiTextBox7.Text = "";
		this.epiTextBox8.Text = "";

		this.epiTextBox1.Value = "";
		this.epiTextBox2.Value = "";
		this.epiTextBox3.Value = "";
		this.epiTextBox4.Value = "";
		this.epiTextBox5.Value = "";
		this.epiTextBox6.Value = "";
		this.epiTextBox7.Value = "";
		this.epiTextBox8.Value = "";

		this.epiCheckBoxC1.Enabled = false;
		this.epiCheckBox1.Enabled = false;
		this.epiCheckBox2.Enabled = false;
		this.epiCheckBox4.Enabled = false;
		this.epiCheckBox6.Enabled = false;
		this.epiCheckBox8.Enabled = false;
		this.epiCheckBox10.Enabled = false;
		this.epiCheckBox12.Enabled = false;

		this.epiTextBox1.ReadOnly = true;
		this.epiTextBox2.ReadOnly = true;
		this.epiTextBox3.ReadOnly = true;
		this.epiTextBox4.ReadOnly = true;
		this.epiTextBox5.ReadOnly = true;
		this.epiTextBox6.ReadOnly = true;
		this.epiTextBox7.ReadOnly = true;
		this.epiTextBox8.ReadOnly = true;

		this.epiTextBoxC2.Text = "";
		this.epiTextBoxC2.Value = "";
	}
}

Nick that looks great. Thank you so much for sharing.

(necro) but we’re working on setting up FAI and it’s not triggering/showing in Inspection Workbench. Does the user need to do the Setup operation as well as Production?

Only the Setup. Did they enter a quantity when logging out?

That explains it then, we don’t do setup they jump right into production. Thanks @jkane

So an inspection processing queue is only created for an Op with FAIQnty when the setup i done on that operation?
Im testing using FAIQnty on the job now and was not able to create a record in inspection processing. but i havent tried clocking to setup yet.

Correct. The way that Epicor is configured, you must do setup on an operation for the FAI to be created.

that seams like a strange place for epicor to trigger the Insp record. we dont use setup up except for our machine shop. Parts are moved to the QC area to do FAI either during the process after an op or at the end so the operators are done clocking at that point.

I am definitely not a Quality expert, but I have worked with some really good quality people.

My understanding of an FAI is that you do not want parts being produced until the FAI is approved. If you try logging into Production, Epicor will stop you if the FAI is not done. You need the Setup to control the FAI and the permissions to run Production. Otherwise, employees could just log into Production and run a bunch of unverified parts that would have to be scrapped.

Additionally, using Setup allows you to quantify how long it took to produce a part that passed FAI. If you are a contract manufacturer, this helps you understand how much to charge for requested FAIs from the customer.

I like that it stops production, but i would think that if the prior op is complete, automatically trigger the FAI queue record for the next op. I dont see the point in clocking set up as the operator that did Op 10 (no FAI) isnt doing Op 20 (FAI) so who should clock the set up if the part is physically sitting in the QC area not even close to Op 20.