fixed manually enter bundle Quantity and Total production and Qr field scan autofocus and Barcode scan Field autofocus

fixed-serach-item
Usama Khan 2025-03-01 05:55:45 -08:00
parent f619001e5f
commit 4ad26f4fa6
12 changed files with 184 additions and 89 deletions

View File

@ -24,9 +24,10 @@ public class JobCardItemDAO {
private final String DELETE_QUERY = String.format( "DELETE FROM %s WHERE id = :id", TABLE_NAME ); private final String DELETE_QUERY = String.format( "DELETE FROM %s WHERE id = :id", TABLE_NAME );
private final String SELECT_BY_CARD_ID = String.format( "SELECT * FROM %s WHERE job_card_id = :card_id", TABLE_NAME ); private final String SELECT_BY_CARD_ID = String.format( "SELECT * FROM %s WHERE job_card_id = :card_id", TABLE_NAME );
private final String SELECT_BY_CARD_ID_AND_ITEM_ID = String.format( "SELECT * FROM %s WHERE job_card_id = :card_id AND item_id = :item_id ", TABLE_NAME ); private final String SELECT_BY_CARD_ID_AND_ITEM_ID = String.format( "SELECT * FROM %s WHERE job_card_id = :card_id AND item_id = :item_id ", TABLE_NAME );
private final String INSERT_QUERY = String.format( "INSERT INTO %s (id, job_card_id, item_id, sku, expected_production, actual_production, total_production, account_id, length, width, gsm, wt_ply, ply) VALUES (:id, :job_card_id, :item_id, :sku, :expected_production, :actual_production, :total_production, :account_id, :length, :width, :gsm, :wt_ply, :ply) ON DUPLICATE KEY UPDATE job_card_id = VALUES(job_card_id), item_id = VALUES(item_id), sku = VALUES(sku), expected_production = VALUES(expected_production), actual_production = VALUES(actual_production), total_production = VALUES(total_production), account_id = VALUES(account_id), length = VALUES(length), width = VALUES(width), gsm = VALUES(gsm), wt_ply = VALUES(wt_ply), ply = VALUES(ply) ", TABLE_NAME ); private final String INSERT_QUERY = String.format( "INSERT INTO %s (id, job_card_id, item_id, sku, expected_production, actual_production, total_production, account_id, length, width, gsm, wt_ply, ply, is_complete) VALUES (:id, :job_card_id, :item_id, :sku, :expected_production, :actual_production, :total_production, :account_id, :length, :width, :gsm, :wt_ply, :ply, :is_complete) ON DUPLICATE KEY UPDATE job_card_id = VALUES(job_card_id), item_id = VALUES(item_id), sku = VALUES(sku), expected_production = VALUES(expected_production), actual_production = VALUES(actual_production), total_production = VALUES(total_production), account_id = VALUES(account_id), length = VALUES(length), width = VALUES(width), gsm = VALUES(gsm), wt_ply = VALUES(wt_ply), ply = VALUES(ply), is_complete =VALUES(is_complete) ", TABLE_NAME );
private final String SELECT_BY_IDS = String.format( "SELECT * FROM %s WHERE id IN (:ids)", TABLE_NAME ); private final String SELECT_BY_IDS = String.format( "SELECT * FROM %s WHERE id IN (:ids)", TABLE_NAME );
private final String SELECT_BY_JOB_CARD_AND_ACCOUNT_IDS = String.format( "SELECT * FROM %s WHERE job_card_id = :job_card_id AND actual_production = :actual_production AND account_id IN (:account_ids)", TABLE_NAME ); private final String SELECT_BY_JOB_CARD_AND_ACCOUNT_IDS = String.format( "SELECT * FROM %s WHERE job_card_id = :job_card_id AND account_id IN (:account_ids) AND is_complete = FALSE ", TABLE_NAME );
private final String SELECT_ALL_ACTIVE_ITEM = String.format("SELECT CASE WHEN MIN(is_complete) = TRUE THEN TRUE ELSE FALSE END FROM %s WHERE job_card_id = :job_card_id AND id IN (:id)", TABLE_NAME);
public JobCardItemDAO(NamedParameterJdbcTemplate namedParameterJdbcTemplate) { public JobCardItemDAO(NamedParameterJdbcTemplate namedParameterJdbcTemplate) {
this.namedParameterJdbcTemplate = namedParameterJdbcTemplate; this.namedParameterJdbcTemplate = namedParameterJdbcTemplate;
@ -47,7 +48,9 @@ public class JobCardItemDAO {
.addValue("width", jobCardItem.getWidth() ) .addValue("width", jobCardItem.getWidth() )
.addValue("gsm", jobCardItem.getGsm() ) .addValue("gsm", jobCardItem.getGsm() )
.addValue("wt_ply", jobCardItem.getWtPly() ) .addValue("wt_ply", jobCardItem.getWtPly() )
.addValue("ply", jobCardItem.getPly() ); .addValue("ply", jobCardItem.getPly() )
.addValue("is_complete", jobCardItem.isComplete() );
return params; return params;
} }
@ -112,12 +115,20 @@ public class JobCardItemDAO {
return namedParameterJdbcTemplate.query( SELECT_BY_IDS, params, new JobCardItemRowMapper() ); return namedParameterJdbcTemplate.query( SELECT_BY_IDS, params, new JobCardItemRowMapper() );
} }
public List<JobCardItem> findByJobCardAndAccountIdsAndIsReceived( Long jobCardId, Long actualProduction, List<Long> accountIds ){ public List<JobCardItem> findByJobCardAndAccountIdsAndIsReceived( Long jobCardId, List<Long> accountIds ){
if( accountIds == null || accountIds.isEmpty() ) return new ArrayList<>(); if( accountIds == null || accountIds.isEmpty() ) return new ArrayList<>();
MapSqlParameterSource params = new MapSqlParameterSource(); MapSqlParameterSource params = new MapSqlParameterSource();
params.addValue( "account_ids", accountIds ); params.addValue( "account_ids", accountIds );
params.addValue("job_card_id", jobCardId ); params.addValue("job_card_id", jobCardId );
params.addValue("actual_production", actualProduction );
return namedParameterJdbcTemplate.query( SELECT_BY_JOB_CARD_AND_ACCOUNT_IDS , params, new JobCardItemRowMapper() ); return namedParameterJdbcTemplate.query( SELECT_BY_JOB_CARD_AND_ACCOUNT_IDS , params, new JobCardItemRowMapper() );
} }
public boolean checkAllItemsComplete( Long jobCardId, List<Long> itemsId ){
if( itemsId == null || itemsId.isEmpty() ) return false;
MapSqlParameterSource params = new MapSqlParameterSource();
params.addValue( "id", itemsId );
params.addValue("job_card_id", jobCardId );
Boolean allComplete = namedParameterJdbcTemplate.queryForObject(SELECT_ALL_ACTIVE_ITEM, params, Boolean.class);
return Boolean.TRUE.equals(allComplete);
}
} }

View File

@ -23,6 +23,7 @@ public class JobCardItem {
private List<CutPiece> cutPieces; private List<CutPiece> cutPieces;
private String title; private String title;
private boolean isSelected; private boolean isSelected;
private boolean isComplete;
public JobCardItem() { public JobCardItem() {
this.expectedProduction = BigDecimal.ZERO; this.expectedProduction = BigDecimal.ZERO;
@ -167,6 +168,14 @@ public class JobCardItem {
isSelected = selected; isSelected = selected;
} }
public boolean isComplete() {
return isComplete;
}
public void setComplete(boolean complete) {
isComplete = complete;
}
@Override @Override
public boolean equals(Object o) { public boolean equals(Object o) {
if (this == o) return true; if (this == o) return true;

View File

@ -9,6 +9,8 @@ public class JobCardItemWrapper {
private long jobCardItemId; private long jobCardItemId;
private List<CutPiece> pieces; private List<CutPiece> pieces;
private BigDecimal actualProduction; private BigDecimal actualProduction;
private int perBundleQuantity;
private boolean finalReceived;
public long getJobCardId() { public long getJobCardId() {
return jobCardId; return jobCardId;
@ -42,6 +44,22 @@ public class JobCardItemWrapper {
this.actualProduction = actualProduction; this.actualProduction = actualProduction;
} }
public int getPerBundleQuantity() {
return perBundleQuantity;
}
public void setPerBundleQuantity(int perBundleQuantity) {
this.perBundleQuantity = perBundleQuantity;
}
public boolean getFinalReceived() {
return finalReceived;
}
public void setFinalReceived(boolean finalReceived) {
this.finalReceived = finalReceived;
}
@Override @Override
public String toString() { public String toString() {
return "JobCardItemWrapper{" + return "JobCardItemWrapper{" +

View File

@ -73,6 +73,7 @@ public class InventoryService {
.collect( Collectors.toMap( JobCardItemWrapper::getJobCardItemId, JobCardItemWrapper::getActualProduction ) ); .collect( Collectors.toMap( JobCardItemWrapper::getJobCardItemId, JobCardItemWrapper::getActualProduction ) );
List<JobCardItem> items = jobCardItemDAO.findByIds( jobCardItemWrapperIds ); List<JobCardItem> items = jobCardItemDAO.findByIds( jobCardItemWrapperIds );
if ( items != null && !items.isEmpty( ) ) { if ( items != null && !items.isEmpty( ) ) {
// get job card item ids // get job card item ids
List<Long> jobCardItemIds = items.stream( ) List<Long> jobCardItemIds = items.stream( )
@ -90,16 +91,38 @@ public class InventoryService {
.collect( Collectors.groupingBy( CutPiece::getJobCardItemId)); .collect( Collectors.groupingBy( CutPiece::getJobCardItemId));
for ( JobCardItem jobCardItem : items) { for ( JobCardItem jobCardItem : items) {
// create + save bundles
List<Bundle> bundles = createBundles( jobCardItem, piecesMap.get( jobCardItem.getId( ) ),jobCardItemIdToActualProdMap.getOrDefault( jobCardItem.getId( ) , BigDecimal.ZERO ) ); int quantity = jobCardItemWrappers.stream()
.filter(e -> e.getJobCardItemId() == jobCardItem.getId())
.mapToInt(JobCardItemWrapper::getPerBundleQuantity)
.findFirst()
.orElse(0);
boolean cuttingComplete = jobCardItemWrappers.stream()
.filter(e -> e.getJobCardItemId() == jobCardItem.getId())
.map(JobCardItemWrapper::getFinalReceived)
.findFirst()
.orElse(false);
BigDecimal production = jobCardItemWrappers.stream()
.filter(e -> e.getJobCardItemId() == jobCardItem.getId())
.map(JobCardItemWrapper::getActualProduction)
.findFirst()
.orElse(BigDecimal.ZERO);
List<Bundle> bundles = createBundles( jobCardItem,quantity,jobCardItemIdToActualProdMap.getOrDefault( jobCardItem.getId( ) , BigDecimal.ZERO ) );
// create transactions // create transactions
createTransactions( bundles, jobCardItem.getAccountId( ), InventoryArtifactType.BUNDLE.name( )); createTransactions( bundles, jobCardItem.getAccountId( ), InventoryArtifactType.BUNDLE.name( ));
jobCardItem.setActualProduction( jobCardItemIdToActualProdMap.getOrDefault( jobCardItem.getId( ) , BigDecimal.ZERO ) ); jobCardItem.setActualProduction( jobCardItemIdToActualProdMap.getOrDefault( jobCardItem.getId( ), BigDecimal.ZERO ).add(jobCardItem.getActualProduction()) );
jobCardItem.setComplete(cuttingComplete);
} }
// update items with quantity // update items with quantity
jobCardItemDAO.saveAll( items ); jobCardItemDAO.saveAll( items );
// update job card inv status // update job card inv status
updateJobCardInventoryStatus( jobCard ); if(jobCardItemDAO.checkAllItemsComplete(jobCardId,jobCardItemIds)) {
updateJobCardInventoryStatus(jobCard);
}
} else { } else {
throw new RuntimeException( "Items Not found in Job Card"); throw new RuntimeException( "Items Not found in Job Card");
} }
@ -182,29 +205,12 @@ public class InventoryService {
// create bundles from cut pieces // create bundles from cut pieces
private List<Bundle> createBundles( JobCardItem jobCardItem, private List<Bundle> createBundles( JobCardItem jobCardItem,
List<CutPiece> jobCardItemPieces, BigDecimal value ) { int perBundleWrap, BigDecimal value ) {
Authentication authentication = SecurityContextHolder.getContext( ).getAuthentication( ); Authentication authentication = SecurityContextHolder.getContext( ).getAuthentication( );
if ( value != null && !value.equals(BigDecimal.ZERO)) { if ( value != null && !value.equals(BigDecimal.ZERO)) {
List<Bundle> bundles = new ArrayList<>( ); List<Bundle> bundles = new ArrayList<>( );
// create bundle against every cut piece
if(value.intValue()<=20){
Bundle bundle = new Bundle( );
bundle.setItemId( jobCardItem.getItemId( ));
bundle.setSku( jobCardItem.getSku( ));
bundle.setJobCardId( jobCardItem.getJobCardId( ) );
bundle.setWrapQuantity(value);
bundle.setType( "BUNDLE");
bundle.setCreatedAt( LocalDateTime.now( ));
bundle.setCreatedBy( authentication.getName( ));
bundles.add( bundle);
bundle.setId( bundleDAO.save( bundle));
bundle.setBarcode( cryptographyService.generateRandomString( 15));
// save again after barcode generation
bundle.setId( bundleDAO.save( bundle));
}else{
int quantity = value.intValue(); int quantity = value.intValue();
int batchSize = 20; int batchSize = perBundleWrap;
int iterations = (int) Math.ceil((double) quantity / batchSize); int iterations = (int) Math.ceil((double) quantity / batchSize);
for (int i = 0; i < iterations; i++) { for (int i = 0; i < iterations; i++) {
@ -224,8 +230,6 @@ public class InventoryService {
bundle.setBarcode( cryptographyService.generateRandomString( 15)); bundle.setBarcode( cryptographyService.generateRandomString( 15));
// save again after barcode generation // save again after barcode generation
bundle.setId( bundleDAO.save( bundle)); bundle.setId( bundleDAO.save( bundle));
}
} }
// for ( Map.Entry<JobCardItem, List<CutPiece>> entry : jobCardItemPieces ) { // for ( Map.Entry<JobCardItem, List<CutPiece>> entry : jobCardItemPieces ) {
// JobCardItem key = entry.getKey( ); // JobCardItem key = entry.getKey( );

View File

@ -184,7 +184,7 @@ public class JobCardService {
.collect( Collectors.toList() ); .collect( Collectors.toList() );
// get items has account ids and has actual production not filled // get items has account ids and has actual production not filled
List<JobCardItem> items = jobCardItemDAO.findByJobCardAndAccountIdsAndIsReceived( id, 0L ,accountIds ); List<JobCardItem> items = jobCardItemDAO.findByJobCardAndAccountIdsAndIsReceived( id ,accountIds );
if (items != null && !items.isEmpty()) { if (items != null && !items.isEmpty()) {
// get job card ite ids // get job card ite ids
List<Long> jobCardItemIds = items.stream() List<Long> jobCardItemIds = items.stream()

View File

@ -90,6 +90,7 @@
}, },
methods : { methods : {
onItemSelect: function (id, item) { onItemSelect: function (id, item) {
console.log("wdwawdwwadwwdwda",item.id)
this.items.push(item); this.items.push(item);
}, },
removeItem: function (index) { removeItem: function (index) {

View File

@ -4,23 +4,27 @@
Vue.prototype.$accounts = window.ctp.accounts; Vue.prototype.$accounts = window.ctp.accounts;
Vue.component('item-rows', { Vue.component('item-rows', {
data() {
return {
wrapQuantity: 0
};
},
props: ['index', 'item'], props: ['index', 'item'],
methods : { methods : {
populateCuttingAccount : function (){ populateCuttingAccount : function (){
return this.$accounts.find(account => account.id === this.item.accountId).title; return this.$accounts.find(account => account.id === this.item.accountId).title;
}, },
handleValueChange : function(value){
this.wrapQuantity = value
}
}, },
template: ` template: `
<tr> <tr>
<td width="400"> <td width="120">
<input hidden="hidden" v-bind:name="'items[' + index + '].jobCardId'" v-bind:value="item.jobCardId" > <input hidden="hidden" v-bind:name="'items[' + index + '].jobCardId'" v-bind:value="item.jobCardId" >
<input hidden="hidden" v-bind:name="'items[' + index + '].jobCardItemId'" v-bind:value="item.id" > <input hidden="hidden" v-bind:name="'items[' + index + '].jobCardItemId'" v-bind:value="item.id" >
<item-search <span class="form-control" readonly>{{item.id}}</span>
v-bind:id-field-name="'items[' + index + '].itemId'"
v-bind:id="item.itemId"
v-bind:type="item.type"
v-bind:show-label="false"
v-bind:disabled="true"></item-search>
</td> </td>
<td width="400"> <td width="400">
<span class="form-control" readonly >{{item.sku}}</span> <span class="form-control" readonly >{{item.sku}}</span>
@ -36,8 +40,17 @@
<td width="200"> <td width="200">
<span class="form-control" readonly>{{item.expectedProduction}}</span> <span class="form-control" readonly>{{item.expectedProduction}}</span>
</td> </td>
<td width="100">
<span class="form-control" readonly>{{item.actualProduction}}</span>
</td>
<td width="200"> <td width="200">
<input class="form-control" min="0" type="number" v-bind:name="'items[' + index + '].actualProduction'" required> <input class="form-control" @input="handleValueChange($event.target.value)" :min="0" type="number" v-bind:name="'items[' + index + '].actualProduction'" required>
</td>
<td width="200">
<input class="form-control" min="1" :max="wrapQuantity" type="number" v-bind:name="'items[' + index + '].perBundleQuantity'" required>
</td>
<td width="10" style="text-align: center;" >
<input type="checkbox" v-bind:name="'items[' + index + '].finalReceived'">
</td> </td>
<td> <td>
{{ populateCuttingAccount() }} {{ populateCuttingAccount() }}
@ -84,12 +97,15 @@
<table class="table table-bordered bg-white w-100"> <table class="table table-bordered bg-white w-100">
<thead> <thead>
<tr> <tr>
<th>Item</th> <th>ID</th>
<th>Item Sku/Title</th> <th>Item Sku/Title</th>
<th>Cut Pieces</th> <th>Cut Pieces</th>
<th>Expected Production</th> <th>Expected Production</th>
<th>Current Production</th>
<th>Actual Production</th> <th>Actual Production</th>
<th style="width: 312px" >Account</th> <th>Per Bundle Quantity</th>
<th>Cutting Complete</th>
<th style="width: 312px" >Account</th>
</tr> </tr>
</tr> </tr>
</thead> </thead>

View File

@ -216,6 +216,7 @@ if ( typeof Vue !== 'undefined' ) {
v-bind:disabled="disabled" v-bind:disabled="disabled"
v-bind:readonly="readOnly" v-bind:readonly="readOnly"
v-bind:inputmode="inputMode" v-bind:inputmode="inputMode"
autofocus
v-bind:class="{ 'is-invalid': ( showInputErrorOnZeroId && ( entityId === 0 || entityId == null ) ) }" v-bind:class="{ 'is-invalid': ( showInputErrorOnZeroId && ( entityId === 0 || entityId == null ) ) }"
autocomplete="off"> autocomplete="off">
<!-- autocomplete list --> <!-- autocomplete list -->
@ -3525,43 +3526,75 @@ if ( typeof Vue !== 'undefined' ) {
}); });
/* Vue.component('search-item', {
* finished item search props: {
* */ label: {
Vue.component('finished-item-search',{ type: String,
mixins: [searchComponentMixin], default: 'Search Finished Item'
methods : {
getSearchUrl : function () {
return `/ctp/rest/finished-items/search?term=${encodeURIComponent( this.list.term )}&is-segregated=${this.isSegregated}`
},
getEmittedEventName: function() {
return 'finished-item-select';
},
getTitle: function( item ) {
return `${item.id} - ( ${item.barcode} )`;
}
}, },
props: { url: {
labelText: { type: String,
default: 'Search Finished Item' default: ''
}, },
titleFieldName: { isSegregated: {
default: 'title' type: Boolean,
}, default: false
idFieldName: { },
default: 'id' debounceTime: {
}, type: Number,
codeFieldName : { default: 500
default : 'code' }
}, },
isSegregated : { data() {
default : false return {
}, searchTerm: '',
inputMode: { timeout: null
default : 'none' };
},
methods: {
handleInput() {
clearTimeout(this.timeout);
this.timeout = setTimeout(() => {
this.fetchData();
}, this.debounceTime);
},
async fetchData() {
if (this.searchTerm.trim() === '') return;
try {
const response = await fetch(`${this.url}?term=${encodeURIComponent(this.searchTerm)}&is-segregated=${this.isSegregated}`);
const data = await response.json();
if (data.length > 0) {
const selectedItem = data[0];
this.$emit('finished-item-select', selectedItem.id, selectedItem);
this.searchTerm = `${selectedItem.id} - (${selectedItem.barcode})`;
setTimeout(() => {
this.searchTerm = '';
}, 500);
} else {
setTimeout(() => {
this.searchTerm = '';
}, 500);
}
} catch (error) {
console.error('Error fetching search results:', error);
} }
} }
}) },
template: `
<div class="search-container">
<label>{{ label }}</label>
<input type="text" class="form-control"
v-model="searchTerm"
@input="handleInput"
placeholder="Scan QR"
autocomplete="off"
inputmode="none"
autofocus>
</div>
`
});
/* /*
* bundle search component * bundle search component

View File

@ -17,9 +17,11 @@
<div class="bg-light p-3 mb-3"> <div class="bg-light p-3 mb-3">
<div class="form-row"> <div class="form-row">
<div class="col-sm-3"> <div class="col-sm-3">
<bundle-search <search-item
v-on:bundle-select="onBundleSelect" label="Search Bundle"
></bundle-search> url="/ctp/rest/bundles/search"
v-on:finished-item-select="onBundleSelect">
</search-item>
</div> </div>
</div> </div>
</div> </div>

View File

@ -22,6 +22,7 @@
v-on:job-card-select="onCardSelect" v-on:job-card-select="onCardSelect"
v-bind:required="true" v-bind:required="true"
></job-card-search> ></job-card-search>
</div> </div>
<!-- <div class="col-sm-3">--> <!-- <div class="col-sm-3">-->
<!-- <label>Cutting Account</label>--> <!-- <label>Cutting Account</label>-->

View File

@ -14,11 +14,10 @@
<div class="bg-light p-3 mb-3"> <div class="bg-light p-3 mb-3">
<div class="form-row"> <div class="form-row">
<div class="col-sm-3 form-group"> <div class="col-sm-3 form-group">
<finished-item-search <search-item
v-bind:is-segregated="false" url="/ctp/rest/finished-items/search"
v-on:finished-item-select="onItemSelect" v-on:finished-item-select="onItemSelect">
> </search-item>
</finished-item-search>
</div> </div>
<div class="col-sm-3 form-group"> <div class="col-sm-3 form-group">
<!-- <label>Packaging Account</label>--> <!-- <label>Packaging Account</label>-->

View File

@ -13,12 +13,13 @@
<form th:action="'/ctp/quality-control/qc-finished-item'" method="post" id="qcForm" <form th:action="'/ctp/quality-control/qc-finished-item'" method="post" id="qcForm"
th:object="${wrapper}"> th:object="${wrapper}">
<div class="bg-light p-3 mb-3"> <div class="bg-light p-3 mb-3">
<h6 class="mb-3">Search Stitched Item</h6>
<div class="form-row"> <div class="form-row">
<div class="col-sm-3 form-group"> <div class="col-sm-3 form-group">
<stitching-offline-item <search-item
v-on:stitching-offline-item-select="onItemSelect" label="Search Stitch Item"
></stitching-offline-item> url="/ctp/rest/stitching-offline-items/search"
v-on:finished-item-select="onItemSelect">
</search-item>
</div> </div>
<div class="col-sm-3 form-group"> <div class="col-sm-3 form-group">
<label>Finishing Account</label> <label>Finishing Account</label>