Merge pull request 'fixed-serach-item' (#12) from fixed-serach-item into main

Reviewed-on: http://git.utopiadeals.com:8080/UIND/cut-to-pack-service/pulls/12
pull/13/head
saif.haq 2025-03-03 05:11:24 +00:00
commit f70ddecfa6
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 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 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_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) {
this.namedParameterJdbcTemplate = namedParameterJdbcTemplate;
@ -47,7 +48,9 @@ public class JobCardItemDAO {
.addValue("width", jobCardItem.getWidth() )
.addValue("gsm", jobCardItem.getGsm() )
.addValue("wt_ply", jobCardItem.getWtPly() )
.addValue("ply", jobCardItem.getPly() );
.addValue("ply", jobCardItem.getPly() )
.addValue("is_complete", jobCardItem.isComplete() );
return params;
}
@ -112,12 +115,20 @@ public class JobCardItemDAO {
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<>();
MapSqlParameterSource params = new MapSqlParameterSource();
params.addValue( "account_ids", accountIds );
params.addValue("job_card_id", jobCardId );
params.addValue("actual_production", actualProduction );
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 String title;
private boolean isSelected;
private boolean isComplete;
public JobCardItem() {
this.expectedProduction = BigDecimal.ZERO;
@ -167,6 +168,14 @@ public class JobCardItem {
isSelected = selected;
}
public boolean isComplete() {
return isComplete;
}
public void setComplete(boolean complete) {
isComplete = complete;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;

View File

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

View File

@ -73,6 +73,7 @@ public class InventoryService {
.collect( Collectors.toMap( JobCardItemWrapper::getJobCardItemId, JobCardItemWrapper::getActualProduction ) );
List<JobCardItem> items = jobCardItemDAO.findByIds( jobCardItemWrapperIds );
if ( items != null && !items.isEmpty( ) ) {
// get job card item ids
List<Long> jobCardItemIds = items.stream( )
@ -90,16 +91,38 @@ public class InventoryService {
.collect( Collectors.groupingBy( CutPiece::getJobCardItemId));
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::getCuttingComplete)
.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
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
jobCardItemDAO.saveAll( items );
// update job card inv status
if(jobCardItemDAO.checkAllItemsComplete(jobCardId,jobCardItemIds)) {
updateJobCardInventoryStatus(jobCard);
}
} else {
throw new RuntimeException( "Items Not found in Job Card");
}
@ -182,29 +205,12 @@ public class InventoryService {
// create bundles from cut pieces
private List<Bundle> createBundles( JobCardItem jobCardItem,
List<CutPiece> jobCardItemPieces, BigDecimal value ) {
int perBundleWrap, BigDecimal value ) {
Authentication authentication = SecurityContextHolder.getContext( ).getAuthentication( );
if ( value != null && !value.equals(BigDecimal.ZERO)) {
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 batchSize = 20;
int batchSize = perBundleWrap;
int iterations = (int) Math.ceil((double) quantity / batchSize);
for (int i = 0; i < iterations; i++) {
@ -225,8 +231,6 @@ public class InventoryService {
// save again after barcode generation
bundle.setId( bundleDAO.save( bundle));
}
}
// for ( Map.Entry<JobCardItem, List<CutPiece>> entry : jobCardItemPieces ) {
// JobCardItem key = entry.getKey( );
// List<CutPiece> value = entry.getValue( );

View File

@ -184,7 +184,7 @@ public class JobCardService {
.collect( Collectors.toList() );
// 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()) {
// get job card ite ids
List<Long> jobCardItemIds = items.stream()

View File

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

View File

@ -4,23 +4,27 @@
Vue.prototype.$accounts = window.ctp.accounts;
Vue.component('item-rows', {
data() {
return {
wrapQuantity: 0
};
},
props: ['index', 'item'],
methods : {
populateCuttingAccount : function (){
return this.$accounts.find(account => account.id === this.item.accountId).title;
},
handleValueChange : function(value){
this.wrapQuantity = value
}
},
template: `
<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 + '].jobCardItemId'" v-bind:value="item.id" >
<item-search
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>
<span class="form-control" readonly>{{item.id}}</span>
</td>
<td width="400">
<span class="form-control" readonly >{{item.sku}}</span>
@ -36,8 +40,17 @@
<td width="200">
<span class="form-control" readonly>{{item.expectedProduction}}</span>
</td>
<td width="100">
<span class="form-control" readonly>{{item.actualProduction}}</span>
</td>
<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 + '].cuttingComplete'">
</td>
<td>
{{ populateCuttingAccount() }}
@ -84,11 +97,14 @@
<table class="table table-bordered bg-white w-100">
<thead>
<tr>
<th>Item</th>
<th>ID</th>
<th>Item Sku/Title</th>
<th>Cut Pieces</th>
<th>Expected Production</th>
<th>Current Production</th>
<th>Actual Production</th>
<th>Per Bundle Quantity</th>
<th>Cutting Complete</th>
<th style="width: 312px" >Account</th>
</tr>
</tr>

View File

@ -216,6 +216,7 @@ if ( typeof Vue !== 'undefined' ) {
v-bind:disabled="disabled"
v-bind:readonly="readOnly"
v-bind:inputmode="inputMode"
autofocus
v-bind:class="{ 'is-invalid': ( showInputErrorOnZeroId && ( entityId === 0 || entityId == null ) ) }"
autocomplete="off">
<!-- autocomplete list -->
@ -3525,43 +3526,75 @@ if ( typeof Vue !== 'undefined' ) {
});
/*
* finished item search
* */
Vue.component('finished-item-search',{
mixins: [searchComponentMixin],
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} )`;
}
},
Vue.component('search-item', {
props: {
labelText: {
label: {
type: String,
default: 'Search Finished Item'
},
titleFieldName: {
default: 'title'
},
idFieldName: {
default: 'id'
},
codeFieldName : {
default : 'code'
url: {
type: String,
default: ''
},
isSegregated: {
type: Boolean,
default: false
},
inputMode: {
default : 'none'
debounceTime: {
type: Number,
default: 500
}
},
data() {
return {
searchTerm: '',
timeout: null
};
},
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

View File

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

View File

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

View File

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

View File

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