This commit is contained in:
@@ -4005,64 +4005,177 @@ namespace AyaNova.Biz
|
||||
//
|
||||
private async Task PartInventoryAdjustmentAsync(AyaEvent ayaEvent, WorkOrderItemPart newObj, WorkOrderItemPart oldObj, IDbContextTransaction transaction)
|
||||
{
|
||||
if (AyaNova.Util.ServerGlobalBizSettings.Cache.UseInventory)
|
||||
{
|
||||
PartInventoryBiz pib = new PartInventoryBiz(ct, UserId, UserTranslationId, CurrentUserRoles);
|
||||
|
||||
//DELETED, HANDLE INVENTORY / SERIALS
|
||||
if (ayaEvent == AyaEvent.Deleted && oldObj.Quantity != 0)
|
||||
PartInventoryBiz pib = new PartInventoryBiz(ct, UserId, UserTranslationId, CurrentUserRoles);
|
||||
|
||||
//DELETED, HANDLE INVENTORY / SERIALS
|
||||
if (ayaEvent == AyaEvent.Deleted && oldObj.Quantity != 0)
|
||||
{
|
||||
//NEGATIVE BLOCK >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
//was originally a return of inventory so needs to consume the inventory back again
|
||||
if (oldObj.Quantity < 0)
|
||||
{
|
||||
//NEGATIVE BLOCK >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
//was originally a return of inventory so needs reversal
|
||||
if (oldObj.Quantity < 0)
|
||||
dtInternalPartInventory pi =
|
||||
new dtInternalPartInventory
|
||||
{
|
||||
PartId = oldObj.PartId,
|
||||
PartWarehouseId = oldObj.PartWarehouseId,
|
||||
Quantity = oldObj.Quantity * -1,//was originally returned (negative) so needs to be consumed
|
||||
SourceType = AyaType.WorkOrderItemPart,
|
||||
SourceId = oldObj.Id,
|
||||
Description = await Translate("WorkOrderItemPart") + $" {oldObj.Serials} " + await Translate("EventDeleted")
|
||||
};
|
||||
if (await pib.CreateAsync(pi) == null)
|
||||
{
|
||||
dtInternalPartInventory pi =
|
||||
new dtInternalPartInventory
|
||||
{
|
||||
PartId = oldObj.PartId,
|
||||
PartWarehouseId = oldObj.PartWarehouseId,
|
||||
Quantity = oldObj.Quantity * -1,//was originally returned so needs to be removed
|
||||
SourceType = AyaType.WorkOrderItemPart,
|
||||
SourceId = oldObj.Id,
|
||||
Description = await Translate("WorkOrderItemPart") + $" {oldObj.Serials} " + await Translate("EventDeleted")
|
||||
};
|
||||
if (await pib.CreateAsync(pi) == null)
|
||||
if (pib.HasErrors)
|
||||
{
|
||||
if (pib.HasErrors)
|
||||
foreach (var e in pib.Errors)
|
||||
{
|
||||
foreach (var e in pib.Errors)
|
||||
{
|
||||
if (e.Code == ApiErrorCode.INSUFFICIENT_INVENTORY)
|
||||
AddError(e.Code, "Quantity", e.Message);
|
||||
else
|
||||
AddError(e.Code, e.Target, e.Message);
|
||||
}
|
||||
if (e.Code == ApiErrorCode.INSUFFICIENT_INVENTORY)
|
||||
AddError(e.Code, "Quantity", e.Message);
|
||||
else
|
||||
AddError(e.Code, e.Target, e.Message);
|
||||
}
|
||||
return;
|
||||
}
|
||||
else
|
||||
{ //Consume serial numbers from part
|
||||
if (!string.IsNullOrWhiteSpace(oldObj.Serials))
|
||||
await PartBiz.RemoveSerialsAsync(oldObj.PartId, oldObj.Serials, ct, UserId);
|
||||
}
|
||||
//<<<<<<<<<<<<<<<<<<<<<< NEGATIVE BLOCK
|
||||
return;
|
||||
}
|
||||
else
|
||||
{ //Consume serial numbers from part
|
||||
if (!string.IsNullOrWhiteSpace(oldObj.Serials))
|
||||
await PartBiz.RemoveSerialsAsync(oldObj.PartId, oldObj.Serials, ct, UserId);
|
||||
}
|
||||
//<<<<<<<<<<<<<<<<<<<<<< NEGATIVE BLOCK
|
||||
}
|
||||
else
|
||||
{
|
||||
//was consumed inventory so return it
|
||||
dtInternalPartInventory pi =
|
||||
new dtInternalPartInventory
|
||||
{
|
||||
PartId = oldObj.PartId,
|
||||
PartWarehouseId = oldObj.PartWarehouseId,
|
||||
Quantity = oldObj.Quantity,
|
||||
SourceType = null,//null because the po no longer exists so this is technically a manual adjustment
|
||||
SourceId = null,//''
|
||||
Description = await Translate("WorkOrderItemPart") + $" {oldObj.Serials} " + await Translate("EventDeleted")
|
||||
};
|
||||
if (await pib.CreateAsync(pi) == null)
|
||||
{
|
||||
//was consumed inventory so return it
|
||||
dtInternalPartInventory pi =
|
||||
new dtInternalPartInventory
|
||||
{
|
||||
PartId = oldObj.PartId,
|
||||
PartWarehouseId = oldObj.PartWarehouseId,
|
||||
Quantity = oldObj.Quantity,
|
||||
SourceType = null,//null because the po no longer exists so this is technically a manual adjustment
|
||||
SourceId = null,//''
|
||||
Description = await Translate("WorkOrderItemPart") + $" {oldObj.Serials} " + await Translate("EventDeleted")
|
||||
};
|
||||
AddError(ApiErrorCode.API_SERVER_ERROR, "generalerror", $"Error updating inventory ({pi.Description}):{pib.GetErrorsAsString()}");
|
||||
return;
|
||||
}
|
||||
else
|
||||
{ //return serial numbers to part
|
||||
if (!string.IsNullOrWhiteSpace(oldObj.Serials))
|
||||
await PartBiz.AppendSerialsAsync(oldObj.PartId, oldObj.Serials, ct, UserId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//CREATED, HANDLE INVENTORY / CONSUME SERIALS
|
||||
if (ayaEvent == AyaEvent.Created && newObj.Quantity != 0)//allow zero quantity parts on workorder as placeholder, serials will not be consumed
|
||||
{
|
||||
|
||||
if (newObj.Quantity < 0)
|
||||
{
|
||||
//NEGATIVE BLOCK >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
//NOTES: new record with negative quantity, this should only be a case where a customer is returning unused parts that were originally consumed by them on a work order
|
||||
//this already requires the inventory full role during validation so if we're here it's allowed to happen
|
||||
//in v7 this was openly allowed by anyone who could make a work order but it was a loophole to circumvent inventory adjustment system
|
||||
//in v8 we allow it only for the narrowly defined pupose of reversing a prior work order consumption of parts but it's not really enforced in code other than requiring a
|
||||
//inventory full role to do it so that the inventory person is at least involved in what's happening and can think it through
|
||||
|
||||
//RETURN INVENTORY
|
||||
dtInternalPartInventory pi =
|
||||
new dtInternalPartInventory
|
||||
{
|
||||
PartId = newObj.PartId,
|
||||
PartWarehouseId = newObj.PartWarehouseId,
|
||||
Quantity = newObj.Quantity,//is negative
|
||||
SourceType = AyaType.WorkOrderItemPart,
|
||||
SourceId = newObj.Id,
|
||||
Description = await Translate("WorkOrderItemPart") + $" {newObj.Serials} " + await Translate("EventCreated")
|
||||
};
|
||||
if (await pib.CreateAsync(pi) == null)
|
||||
{
|
||||
if (pib.HasErrors)
|
||||
{
|
||||
AddError(ApiErrorCode.API_SERVER_ERROR, "generalerror", $"Error updating inventory ({pi.Description}):{pib.GetErrorsAsString()}");
|
||||
foreach (var e in pib.Errors)
|
||||
{
|
||||
if (e.Code == ApiErrorCode.INSUFFICIENT_INVENTORY)
|
||||
AddError(e.Code, "Quantity", e.Message);
|
||||
else
|
||||
AddError(e.Code, e.Target, e.Message);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
else
|
||||
{ //Append serial numbers from part
|
||||
if (!string.IsNullOrWhiteSpace(newObj.Serials))
|
||||
await PartBiz.AppendSerialsAsync(newObj.PartId, newObj.Serials, ct, UserId);
|
||||
}
|
||||
|
||||
//<<<<<<<<<<<<<<<<<<<<<< NEGATIVE BLOCK
|
||||
}
|
||||
else
|
||||
{
|
||||
//CONSUME INVENTORY
|
||||
dtInternalPartInventory pi =
|
||||
new dtInternalPartInventory
|
||||
{
|
||||
PartId = newObj.PartId,
|
||||
PartWarehouseId = newObj.PartWarehouseId,
|
||||
Quantity = newObj.Quantity * -1,
|
||||
SourceType = AyaType.WorkOrderItemPart,
|
||||
SourceId = newObj.Id,
|
||||
Description = await Translate("WorkOrderItemPart") + $" {newObj.Serials} " + await Translate("EventCreated")
|
||||
};
|
||||
if (await pib.CreateAsync(pi) == null)
|
||||
{
|
||||
if (pib.HasErrors)
|
||||
{
|
||||
foreach (var e in pib.Errors)
|
||||
{
|
||||
if (e.Code == ApiErrorCode.INSUFFICIENT_INVENTORY)
|
||||
AddError(e.Code, "Quantity", e.Message);
|
||||
else
|
||||
AddError(e.Code, e.Target, e.Message);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
else
|
||||
{ //Consume serial numbers from part
|
||||
if (!string.IsNullOrWhiteSpace(newObj.Serials))
|
||||
await PartBiz.RemoveSerialsAsync(newObj.PartId, newObj.Serials, ct, UserId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//UPDATED, HANDLE INVENTORY / UPDATE SERIALS AS REQUIRED
|
||||
if (ayaEvent == AyaEvent.Modified)
|
||||
{
|
||||
//INVENTORY
|
||||
if (newObj.PartId != oldObj.PartId || newObj.Quantity != oldObj.Quantity)
|
||||
{
|
||||
//OUT with the old
|
||||
if (oldObj.Quantity != 0)//zero quantity doesn't affect inventory or serials
|
||||
{
|
||||
dtInternalPartInventory piOld = new dtInternalPartInventory
|
||||
{
|
||||
PartId = oldObj.PartId,
|
||||
PartWarehouseId = oldObj.PartWarehouseId,
|
||||
Quantity = oldObj.Quantity,
|
||||
SourceType = null,//null because the po no longer exists so this is technically a manual adjustment
|
||||
SourceId = null,//''
|
||||
Description = await Translate("WorkOrderItemPart") + $" {oldObj.Serials} " + await Translate("EventDeleted")
|
||||
};
|
||||
if (await pib.CreateAsync(piOld) == null)
|
||||
{
|
||||
AddError(ApiErrorCode.API_SERVER_ERROR, "generalerror", $"Error updating inventory ({piOld.Description}):{pib.GetErrorsAsString()}");
|
||||
return;
|
||||
}
|
||||
else
|
||||
@@ -4072,56 +4185,22 @@ namespace AyaNova.Biz
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
//CREATED, HANDLE INVENTORY / CONSUME SERIALS
|
||||
if (ayaEvent == AyaEvent.Created && newObj.Quantity != 0)//allow zero quantity parts on workorder as placeholder, serials will not be consumed
|
||||
{
|
||||
|
||||
if (newObj.Quantity < 0)
|
||||
{
|
||||
//NEGATIVE BLOCK >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
|
||||
//RETURN INVENTORY
|
||||
dtInternalPartInventory pi =
|
||||
new dtInternalPartInventory
|
||||
{
|
||||
PartId = newObj.PartId,
|
||||
PartWarehouseId = newObj.PartWarehouseId,
|
||||
Quantity = newObj.Quantity * -1,
|
||||
SourceType = AyaType.WorkOrderItemPart,
|
||||
SourceId = newObj.Id,
|
||||
Description = await Translate("WorkOrderItemPart") + $" {newObj.Serials} " + await Translate("EventCreated")
|
||||
};
|
||||
|
||||
//<<<<<<<<<<<<<<<<<<<<<< NEGATIVE BLOCK
|
||||
}
|
||||
else
|
||||
{
|
||||
//CONSUME INVENTORY
|
||||
dtInternalPartInventory pi =
|
||||
new dtInternalPartInventory
|
||||
{
|
||||
PartId = newObj.PartId,
|
||||
PartWarehouseId = newObj.PartWarehouseId,
|
||||
Quantity = newObj.Quantity * -1,
|
||||
SourceType = AyaType.WorkOrderItemPart,
|
||||
SourceId = newObj.Id,
|
||||
Description = await Translate("WorkOrderItemPart") + $" {newObj.Serials} " + await Translate("EventCreated")
|
||||
};
|
||||
if (await pib.CreateAsync(pi) == null)
|
||||
//IN with the new
|
||||
if (newObj.Quantity != 0)
|
||||
{//NOTE: zero quantity is considered to be a placeholder and no serials will be consumed, nor inventory affected
|
||||
dtInternalPartInventory piNew = new dtInternalPartInventory
|
||||
{
|
||||
if (pib.HasErrors)
|
||||
{
|
||||
foreach (var e in pib.Errors)
|
||||
{
|
||||
if (e.Code == ApiErrorCode.INSUFFICIENT_INVENTORY)
|
||||
AddError(e.Code, "Quantity", e.Message);
|
||||
else
|
||||
AddError(e.Code, e.Target, e.Message);
|
||||
}
|
||||
}
|
||||
PartId = newObj.PartId,
|
||||
PartWarehouseId = newObj.PartWarehouseId,
|
||||
Quantity = newObj.Quantity * -1,
|
||||
SourceType = AyaType.WorkOrderItemPart,
|
||||
SourceId = newObj.Id,
|
||||
Description = await Translate("WorkOrderItemPart") + $" {newObj.Serials} " + await Translate("EventCreated")
|
||||
};
|
||||
|
||||
if (await pib.CreateAsync(piNew) == null)
|
||||
{
|
||||
AddError(ApiErrorCode.API_SERVER_ERROR, "generalerror", $"Error updating inventory ({piNew.Description}):{pib.GetErrorsAsString()}");
|
||||
return;
|
||||
}
|
||||
else
|
||||
@@ -4131,78 +4210,21 @@ namespace AyaNova.Biz
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//UPDATED, HANDLE INVENTORY / UPDATE SERIALS AS REQUIRED
|
||||
if (ayaEvent == AyaEvent.Modified)
|
||||
//SERIALS
|
||||
else if (newObj.Serials != oldObj.Serials)
|
||||
{
|
||||
//INVENTORY
|
||||
if (newObj.PartId != oldObj.PartId || newObj.Quantity != oldObj.Quantity)
|
||||
{
|
||||
//OUT with the old
|
||||
if (oldObj.Quantity != 0)//zero quantity doesn't affect inventory or serials
|
||||
{
|
||||
dtInternalPartInventory piOld = new dtInternalPartInventory
|
||||
{
|
||||
PartId = oldObj.PartId,
|
||||
PartWarehouseId = oldObj.PartWarehouseId,
|
||||
Quantity = oldObj.Quantity,
|
||||
SourceType = null,//null because the po no longer exists so this is technically a manual adjustment
|
||||
SourceId = null,//''
|
||||
Description = await Translate("WorkOrderItemPart") + $" {oldObj.Serials} " + await Translate("EventDeleted")
|
||||
};
|
||||
if (await pib.CreateAsync(piOld) == null)
|
||||
{
|
||||
AddError(ApiErrorCode.API_SERVER_ERROR, "generalerror", $"Error updating inventory ({piOld.Description}):{pib.GetErrorsAsString()}");
|
||||
return;
|
||||
}
|
||||
else
|
||||
{ //return serial numbers to part
|
||||
if (!string.IsNullOrWhiteSpace(oldObj.Serials))
|
||||
await PartBiz.AppendSerialsAsync(oldObj.PartId, oldObj.Serials, ct, UserId);
|
||||
}
|
||||
}
|
||||
//NOTE: zero quantity is considered to be a placeholder and no serials will be consumed (hence not returned either)
|
||||
|
||||
//IN with the new
|
||||
if (newObj.Quantity != 0)
|
||||
{//NOTE: zero quantity is considered to be a placeholder and no serials will be consumed, nor inventory affected
|
||||
dtInternalPartInventory piNew = new dtInternalPartInventory
|
||||
{
|
||||
PartId = newObj.PartId,
|
||||
PartWarehouseId = newObj.PartWarehouseId,
|
||||
Quantity = newObj.Quantity * -1,
|
||||
SourceType = AyaType.WorkOrderItemPart,
|
||||
SourceId = newObj.Id,
|
||||
Description = await Translate("WorkOrderItemPart") + $" {newObj.Serials} " + await Translate("EventCreated")
|
||||
};
|
||||
//return serial numbers to part
|
||||
if (oldObj.Quantity != 0 && !string.IsNullOrWhiteSpace(oldObj.Serials))
|
||||
await PartBiz.AppendSerialsAsync(oldObj.PartId, oldObj.Serials, ct, UserId);
|
||||
|
||||
if (await pib.CreateAsync(piNew) == null)
|
||||
{
|
||||
AddError(ApiErrorCode.API_SERVER_ERROR, "generalerror", $"Error updating inventory ({piNew.Description}):{pib.GetErrorsAsString()}");
|
||||
return;
|
||||
}
|
||||
else
|
||||
{ //Consume serial numbers from part
|
||||
if (!string.IsNullOrWhiteSpace(newObj.Serials))
|
||||
await PartBiz.RemoveSerialsAsync(newObj.PartId, newObj.Serials, ct, UserId);
|
||||
}
|
||||
}
|
||||
}
|
||||
//SERIALS
|
||||
else if (newObj.Serials != oldObj.Serials)
|
||||
{
|
||||
//NOTE: zero quantity is considered to be a placeholder and no serials will be consumed (hence not returned either)
|
||||
|
||||
//return serial numbers to part
|
||||
if (oldObj.Quantity != 0 && !string.IsNullOrWhiteSpace(oldObj.Serials))
|
||||
await PartBiz.AppendSerialsAsync(oldObj.PartId, oldObj.Serials, ct, UserId);
|
||||
|
||||
//Consume serial numbers from part
|
||||
if (newObj.Quantity != 0 && !string.IsNullOrWhiteSpace(newObj.Serials))
|
||||
await PartBiz.RemoveSerialsAsync(newObj.PartId, newObj.Serials, ct, UserId);
|
||||
}
|
||||
//Consume serial numbers from part
|
||||
if (newObj.Quantity != 0 && !string.IsNullOrWhiteSpace(newObj.Serials))
|
||||
await PartBiz.RemoveSerialsAsync(newObj.PartId, newObj.Serials, ct, UserId);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -4253,8 +4275,9 @@ namespace AyaNova.Biz
|
||||
return;
|
||||
}
|
||||
|
||||
if (proposedObj.Quantity < 0 && !UserHasInventoryFullRole)//negative quantities are not allowed unless the user has inventory full role
|
||||
AddError(ApiErrorCode.VALIDATION_INVALID_VALUE, "Quantity", await Translate("InventoryRoleRequired"));
|
||||
if (AyaNova.Util.ServerGlobalBizSettings.Cache.UseInventory)
|
||||
if (proposedObj.Quantity < 0 && !UserHasInventoryFullRole)//negative quantities are not allowed unless the user has inventory full role
|
||||
AddError(ApiErrorCode.VALIDATION_INVALID_VALUE, "Quantity", await Translate("InventoryRoleRequired"));
|
||||
|
||||
//Any form customizations to validate?
|
||||
var FormCustomization = await ct.FormCustom.AsNoTracking().SingleOrDefaultAsync(z => z.FormKey == AyaType.WorkOrderItemPart.ToString());
|
||||
|
||||
Reference in New Issue
Block a user