001
002 package market;
003
004 import java.util.Hashtable;
005 import java.util.Iterator;
006
007 import market.stdform.ButtonIDs;
008 import market.stdform.FSCheckable;
009 import market.stdform.FSWorkerDefault;
010 import market.stdform.FSWorkerEdit;
011 import market.stdform.FSWorkerOrder;
012 import market.stdform.MSLogOff;
013 import sale.FormSheet;
014 import sale.Gate;
015 import sale.GateChangeTransition;
016 import sale.SaleProcess;
017 import sale.SalesPoint;
018 import sale.Shop;
019 import sale.Transition;
020 import sale.UIGate;
021 import sale.stdforms.MsgForm;
022 import users.User;
023 import users.UserManager;
024 import data.StockItem;
025 import data.events.VetoException;
026 import data.ooimpl.CountingStockImpl;
027 import data.stdforms.SingleTableFormSheet;
028
029 /**
030 * The worker process. This process handles execution of the orders.
031 */
032 public class SProcessWorker extends SProcessMarket{
033
034 /**
035 * ID for serialization.
036 */
037 private static final long serialVersionUID = -5031583540761145768L;
038
039 public static final int BUYPROCESS = 0;
040 public static final int TILLQUEUE = 1;
041 public static final int WAREHOUSEQUEUE = 2;
042 public static final int OLDOFFER = 3;
043
044 /**
045 * Gate for displaying how much workers are currently logged on and
046 * how much orders aren`t yet executed.
047 */
048 private UIGate uig_initial = new UIGate(null, null);
049
050 /**
051 * Gate for displaying a checklist for the order of the currently selected customer.
052 */
053 private UIGate uig_order = new UIGate(null, null);
054
055 /**
056 * Gate for displaying that the execution of the order isn`t completed.
057 */
058 private UIGate uig_notReady = new UIGate(null, null);
059
060 /**
061 * Gate for editing the count of articles in case of a shortage.
062 */
063 private UIGate uig_edit = new UIGate(null, null);
064
065 /**
066 * Gate for displaying that no article was selected.
067 */
068 private UIGate uig_noItemSelected = new UIGate(null, null);
069
070 /**
071 * Gate for affirming that the correction of the article`s count was successful.
072 */
073 private UIGate uig_editConfirmation = new UIGate(null, null);
074
075 private SingleTableFormSheet stfs_order;
076 private FSCheckable fsc_edit;
077
078 /**
079 * Current {@link CSOrder} to execute.
080 */
081 private CSOrder cso_order = null;
082
083 /**
084 * A new order that might contains articles which aren`t yet avaible in case of a shortage.
085 */
086 private CountingStockImpl cs_newOrder;
087
088 /**
089 * Current selected [@link SICustomer}.
090 */
091 private SICustomer sic_customer = null;
092
093 /**
094 * The global warehouse-queue.
095 */
096 private static SSListenable ss_orders = SMarket.getWarehouseQueue();
097
098 /**
099 * A Hashtable storing which articles are already executed and which not.
100 */
101 private Hashtable<CIArticle, Object> h_articlesDone = new Hashtable<CIArticle, Object>();
102
103 /**
104 * The {@link CIArticle} which count has to be edit.
105 */
106 private CIArticle ci_article = null;
107
108 /**
109 * An Array containing the count of an article in all different datastructures in the market,
110 * temporarily items in shoppingbaskets, orders waiting at the till-queue,
111 * orders waiting at the warehouse-queue and items in the markets-offer.
112 */
113 private int[] databaseCount = new int[4];
114
115
116 /**
117 * @param sName the name of the process.
118 */
119 public SProcessWorker(String sName) {
120 super(sName);
121 }
122
123
124 //################################### Gates ############################################################
125
126 /**
127 * Attaches {@link FSWorkerDefault}, its actions and the menu to {@link #uig_initial}.
128 * @return the set up {@link #uig_initial}.
129 */
130 protected Gate getInitialGate() {
131 FormSheet fs = new FSWorkerDefault(getOrderCount(), getWorkerCount());
132 setTransition(fs, changeToOrderGate(), ButtonIDs.BTN_OK);
133 uig_initial.setFormSheet(fs);
134 uig_initial.setMenuSheet(new MSLogOff(logOff()));
135 return uig_initial;
136 }
137
138 /**
139 * Attaches {@link FSWorkerOrder} and its actions to {@link #uig_order}.
140 * @return the set up {@link #uig_order}.
141 */
142 private Gate getOrderGate(){
143 stfs_order = FSWorkerOrder.getOrderTable(sic_customer.getName(), cso_order, h_articlesDone);
144 setTransition(stfs_order, GateChangeTransition.CHANGE_TO_COMMIT_GATE, ButtonIDs.BTN_ACCEPT);
145 setTransition(stfs_order, changeToEditGate(), ButtonIDs.BTN_EDIT);
146 setTransition(stfs_order, GateChangeTransition.CHANGE_TO_ROLLBACK_GATE, ButtonIDs.BTN_BACK);
147 uig_order.setFormSheet(stfs_order);
148 return uig_order;
149 }
150
151 /**
152 * Attaches {@link MsgForm} and its ok-action to {@link #uig_notReady}.
153 * @return the set up {@link #uig_notReady}.
154 */
155 private Gate notReadyGate(){
156 FormSheet fs = new MsgForm("Auftrag nicht fertig","Sie haben nicht alle Artikel abgearbeitet.\n"+
157 "Bitte erledigen Sie dies zunächst damit der Auftrag abgeschlossen werden kann!");
158 setTransition(fs, new GateChangeTransition(uig_order), FormSheet.BTNID_OK);
159 uig_notReady.setFormSheet(fs);
160 return uig_notReady;
161 }
162
163 /**
164 * Attaches {@link FSWorkerEdit} and its actions to {@link #uig_edit}.
165 * @return the set up {@link #uig_edit}.
166 */
167 private Gate getEditGate(){
168 fsc_edit = FSWorkerEdit.create(ci_article, getDatabaseCount());
169 setAction(fsc_edit, new sale.Action(){
170 private static final long serialVersionUID = -3206671846726678734L;
171
172 public void doAction(SaleProcess p, SalesPoint sp) throws Throwable {
173 if(fsc_edit.checkTextFields(FSCheckable.ALL_ERRORMESSAGES_AT_ONCE, false)){
174 uig_edit.setNextTransition(changeToEditConfirmationGate());
175 }
176 }
177 }, ButtonIDs.BTN_OK);
178 setTransition(fsc_edit, new GateChangeTransition(getOrderGate()), ButtonIDs.BTN_BACK);
179 uig_edit.setFormSheet(fsc_edit);
180 return uig_edit;
181 }
182
183 /**
184 * Attaches {@link MsgForm} and its ok-action to {@link #uig_noItemSelected}.
185 * @return the set up {@link #uig_noItemSelected}.
186 */
187 private Gate getNoItemSelectedGate(){
188 FormSheet fs = new MsgForm("Kein Artikel gewählt", "Sie müssen zunächst einen Artikel auswählen!");
189 setTransition(fs, new GateChangeTransition(getOrderGate()), FormSheet.BTNID_OK);
190 uig_noItemSelected.setFormSheet(fs);
191 return uig_noItemSelected;
192 }
193
194 /**
195 * Attaches {@link MsgForm} and its ok-action to {@link #uig_editConfirmation}.
196 * @return the set up {@link #uig_editConfirmation}.
197 */
198 private Gate getEditConfirmationGate(){
199 FormSheet fs = new MsgForm("Bestand aktualisiert", "Der Bestand wurde erfolgreich korrigiert!");
200 setTransition(fs, new GateChangeTransition(getOrderGate()), FormSheet.BTNID_OK);
201 uig_editConfirmation.setFormSheet(fs);
202 return uig_editConfirmation;
203 }
204
205 /**
206 * @return the Gate to jump to if the current order is complete.
207 */
208 public Gate getCommitGate() {
209 return new Gate(){
210 private static final long serialVersionUID = 7420935906435390919L;
211
212 public Transition getNextTransition(SaleProcess pOwner, User usr)
213 throws InterruptedException {
214 return commit();
215 }
216 };
217 }
218
219 /**
220 * @return the Gate to jump to if the current order has to be rolled back.
221 */
222 public Gate getRollbackGate() {
223 return new Gate(){
224 private static final long serialVersionUID = -4697158069254762692L;
225
226 public Transition getNextTransition(SaleProcess pOwner, User usr)
227 throws InterruptedException {
228 return rollback();
229 }
230 };
231 }
232
233 //################################## Transitions ##########################################################
234
235 /**
236 * @return a Transition that changes to the {@link #getOrderGate()} if there are any orders left,
237 * otherwise returns back to {@link #getInitialGate()},
238 * sets the next customer and order.
239 */
240 private Transition changeToOrderGate(){
241 return new Transition(){
242 private static final long serialVersionUID = -8976232730398727428L;
243
244 public Gate perform(SaleProcess pOwner, User usr) {
245 setNextCustomer();
246 if(sic_customer==null) return getInitialGate();
247 try {
248 ss_orders.remove(sic_customer, pOwner.getBasket());
249 } catch (VetoException e) {
250 System.err.println(e.getMessage());
251 return getInitialGate();
252 }
253 cso_order = sic_customer.removeOrderFromQueue();
254 initiateArticlesDone();
255 return getOrderGate();
256 }
257 };
258 }
259
260 /**
261 * @return a Transition that changes to the {@link #getInitialGate()} if order is completed,
262 * otherwise changes to the {@link #notReadyGate()},
263 * adds a new CSOrder to the customer if some articles were not avaible.
264 */
265 private Transition commit(){
266 return new Transition(){
267 private static final long serialVersionUID = -2854924689394242526L;
268
269 public Gate perform(SaleProcess pOwner, User usr) {
270 if(h_articlesDone.contains(Boolean.FALSE)) return notReadyGate();
271 if(cs_newOrder != null) sic_customer.addOrderToQueue(cs_newOrder, false);
272 if(sic_customer.getOrderCount(false)==0) getBasket().commit();
273 else getBasket().rollback();
274 Shop.getTheShop().removeStock(cso_order.getName());
275 sic_customer = null;
276 cso_order = null;
277 cs_newOrder = null;
278 h_articlesDone.clear();
279 return getInitialGate();
280 }
281 };
282 }
283
284 /**
285 * @return a Transition that changes to the {@link #getInitialGate()},
286 * rolls back the previously selected order.
287 */
288 private Transition rollback(){
289 return new Transition(){
290 private static final long serialVersionUID = -4226978331728960463L;
291
292 public Gate perform(SaleProcess pOwner, User usr) {
293 pOwner.getBasket().rollback();
294 sic_customer.rollbackOrder(cso_order);
295 sic_customer = null;
296 cso_order = null;
297 h_articlesDone.clear();
298 ci_article = null;
299 cs_newOrder = null;
300 return getInitialGate();
301 }
302 };
303 }
304
305 /**
306 * @return a Transition that changes to the {@link #getEditGate()} if an article was choosed,
307 * otherwise changes to the {@link #getNoItemSelectedGate()}.
308 */
309 private Transition changeToEditGate(){
310 return new Transition(){
311 private static final long serialVersionUID = 6357200452359202447L;
312
313 public Gate perform(SaleProcess pOwner, User usr) {
314 if(stfs_order.getSelectedRecord()==null) return getNoItemSelectedGate();
315 ci_article = Conversions.recordToCIArticle(stfs_order.getSelectedRecord());
316 countDatabase();
317 return getEditGate();
318 }
319 };
320 }
321
322 /**
323 * @return a Transition that changes to the {@link #getEditConfirmationGate()},
324 * corrects the count of the currently selected article in market`s dates.
325 */
326 private Transition changeToEditConfirmationGate(){
327 return new Transition(){
328 private static final long serialVersionUID = -2296457263704403221L;
329
330 public Gate perform(SaleProcess pOwner, User usr) {
331 // Checks whether the new number of articles is smaller than the old one
332 // because if their is no shortage there is no need to update
333 // this can be done during an inventure or something like this
334 if(Integer.parseInt(fsc_edit.getEntry(FSWorkerEdit.JTFC_REAL))<=getDatabaseCount()){
335 updateOffer();
336 }
337 return getEditConfirmationGate();
338 }
339 };
340 }
341
342 /**
343 * @return a Transition that changes to the {@link #getStopGate()},
344 * fires Event: updateWorkerScreen to all MarketEventListeners
345 */
346 private Transition logOff(){
347 return new Transition(){
348 private static final long serialVersionUID = 3092364801488034964L;
349
350 public Gate perform(SaleProcess pOwner, User usr) {
351 SMarket.fireUpdateWorkerScreen();
352 return getStopGate();
353 }
354 };
355 }
356
357 //#################################### internal methods #####################################################
358
359 /**
360 * @return the count of all workers which are currently logged on.
361 */
362 private static int getWorkerCount(){
363 int worker = 0;
364 Iterator salespoints = Shop.getTheShop().getSalesPoints().iterator();
365 while(salespoints.hasNext()){
366 User user = ((SalesPoint)salespoints.next()).getUser();
367 if(((UMUserBase)UserManager.getGlobalUM()).getWarehouseWorker().match(user)){
368 worker++;
369 }
370 }
371 return worker;
372 }
373
374 /**
375 * @return the count of all orders which aren`t completed yet.
376 */
377 private static int getOrderCount(){
378 int order = 0;
379 Iterator it = ss_orders.iterator(null, false);
380 while(it.hasNext()){
381 order = order + ((SICustomer)it.next()).getOrderCount(true);
382 }
383 return order;
384 }
385
386 /**
387 * Selects the next customer and order to be execute.
388 */
389 private void setNextCustomer(){
390 Long time = new Long(System.currentTimeMillis());
391 SICustomer sic_next = null;
392 Iterator it = ss_orders.iterator(null, true);
393 while(it.hasNext()){
394 SICustomer sic_current = (SICustomer)it.next();
395 if((time.compareTo(sic_current.getOrderQueueTime())>=0) && (sic_current.getOrderCount(true) > 0)){
396 time = sic_current.getOrderQueueTime();
397 sic_next = sic_current;
398 }
399 }
400 sic_customer = sic_next;
401 }
402
403 /**
404 * Initiates the HashTable with false in the value-fields to signal the articles aren`t executed yet.
405 */
406 private void initiateArticlesDone(){
407 Iterator<StockItem> it = cso_order.iterator(null, false);
408 while(it.hasNext()){
409 h_articlesDone.put((CIArticle) it.next().getAssociatedItem(null), Boolean.FALSE);
410 }
411 }
412
413 /**
414 * @return the count of the selected article currently stored in the market`s database.
415 */
416 private int getDatabaseCount(){
417 return databaseCount[BUYPROCESS]+
418 databaseCount[TILLQUEUE]+
419 databaseCount[WAREHOUSEQUEUE]+
420 databaseCount[OLDOFFER];
421 }
422
423 /**
424 * Adds a given value to the value at the specified position,
425 * used by different implementations of OfferEventListener
426 * to find out the count of an article considering all datastructures of the market.
427 *
428 * @param value the value that will be added.
429 * @param type the index of the array at which the value will be added,
430 * use {@link #BUYPROCESS} to {@link #OLDOFFER}.
431 */
432 public synchronized void addDatabaseCount(int value, int type){
433 databaseCount[type] += value;
434 }
435
436 /**
437 * Sets {@link #databaseCount} to the current count of the selected article,
438 * uses Event: countArticles of OfferEventListener.
439 */
440 private void countDatabase(){
441 databaseCount = new int[4];
442 SPCustomer.fireCountArticles(ci_article.getName(), this);
443 SMarket.getTillQueue().fireCountArticles(ci_article.getName(), this);
444 SMarket.getWarehouseQueue().fireCountArticles(ci_article.getName(), this);
445 addDatabaseCount(cso_order.countItems(ci_article.getName(), null), WAREHOUSEQUEUE);
446 addDatabaseCount(SMarket.getOffer().countItems(ci_article.getName(), null), OLDOFFER);
447 }
448
449 /**
450 * Updates the count of the selected article in market`s database,
451 * if necessary fires Event: offerIsEmpty to OfferEventListeners.
452 */
453 private void updateOffer(){
454 String aKey = ci_article.getName();
455 int realCount = Integer.parseInt(fsc_edit.getEntry(FSWorkerEdit.JTFC_REAL));
456 int newCount = databaseCount[OLDOFFER] + databaseCount[BUYPROCESS]
457 + databaseCount[TILLQUEUE] + databaseCount[WAREHOUSEQUEUE] - realCount;
458 //if real count of the article doesn`t reach for orders at the till and warehouse
459 //and selections of the customers, then first temporarily selected articles will be rolled back
460 //by firing event: offerIsEmpty to {@link SPCustomer}
461 if(realCount < (databaseCount[BUYPROCESS] + databaseCount[TILLQUEUE] + databaseCount[WAREHOUSEQUEUE])){
462 SPCustomer.fireOfferIsEmpty(aKey);
463 //if real count still doesn`t reach, the article will be removed from orders at the till-queue
464 //by firing event: offerIsEmpty to {@link SICustomer}s at the till-queue
465 if(realCount < (databaseCount[TILLQUEUE] + databaseCount[WAREHOUSEQUEUE])){
466 SMarket.getTillQueue().fireOfferIsEmpty(aKey);
467 //if real count still doesn`t reach, the article will be removed from orders
468 //at the warehouse-queue,
469 //by firing event: offerIsEmpty to {@link SICustomer}s at the warehouse-queue
470 if(realCount<databaseCount[WAREHOUSEQUEUE]){
471 SMarket.getWarehouseQueue().fireOfferIsEmpty(aKey);
472 //if real count doesn`t reach for the current order,
473 //the missing one will be added as a new inactive order to the warehouse-queue
474 if(realCount < cso_order.countItems(aKey, null)){
475 int i = cso_order.countItems(aKey, null) - realCount;
476 cso_order.remove(aKey, i, null);
477 if(cs_newOrder == null){
478 cs_newOrder = new CountingStockImpl("new Order", SMarket.getArticleCatalog());
479 }
480 cs_newOrder.add(aKey, i, null);
481 if(!cso_order.contains(aKey, null)) h_articlesDone.remove(ci_article);
482 newCount -= i;
483 }
484 }
485 }
486
487 }
488 if(newCount <= 0) newCount = SMarket.getOffer().countItems(aKey, null);
489 if(newCount > 0){
490 try {
491 SMarket.getOffer().remove(aKey, newCount, null);
492 } catch (Exception e) {
493 System.out.println(e.getMessage());
494 }
495 }
496 }
497 }