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