001    import sale.*;
002    import sale.stdforms.*;
003    import data.*;
004    import data.ooimpl.*;
005    import data.stdforms.*;
006    import users.*;
007    import log.*;
008    
009    import java.util.*;
010    import java.text.*;
011    import java.lang.*;
012    import java.io.*;
013    
014    import javax.swing.JTextField;
015    import javax.swing.JPanel;
016    import javax.swing.JOptionPane;
017    import javax.swing.BoxLayout;
018    import javax.swing.JLabel;
019    
020    
021    
022    /**
023     * Verkaufs- bzw. Verleihprozess, der von einem registrierten Kunden
024     * am Automaten durchgeführt werden kann.
025     */
026    public class RentProcess extends SaleProcess
027    {
028       
029      //// attributes ////////////////////////////////////////////////////////////
030       
031      // Gates
032      protected UIGate capabilityGate;
033      protected UIGate selectionGate;
034      protected UIGate rentGate;
035      protected Gate   decisionGate;
036      protected UIGate getChangeGate;
037       
038      // Transitions
039      protected Transition toSelectionTransition;
040      protected Transition toPayingTransition;
041      protected Transition toDecisionTransition;
042      protected Transition toGetChangeTransition;
043      
044      // verwendete Waehrung
045      private Currency myCurrency;
046      
047      // zu zahlender Betrag
048      private IntegerValue toPayValue;
049       
050      // gezahlter Betrag
051      private IntegerValue paidValue;
052       
053      // Bewertung der Relation zwischen zu zahlendem und gezahltem Betrag
054      private int payAssessment;
055       
056      // Kunde, der ausleihen moechte
057      private Customer customer;
058      
059      // der Iterator ueber die geliehenen Videos
060      private transient Iterator videoIterator;
061       
062       
063      //// constructor ///////////////////////////////////////////////////////////
064       
065      /**
066       * Erzeugt ein neues Objekt der Klasse RentProcess.
067       */
068      public RentProcess()
069      {
070        super ("RentProcess");
071      }
072      
073      //// protected methods /////////////////////////////////////////////////////
074       
075      /**
076       * Baut die Oberfläche für den Verleihvorgang auf.
077       */
078      protected void setupMachine()
079      {
080        // verwendete Waehrung ermitteln
081        myCurrency = (Currency)Shop.getTheShop().getCatalog("DM");
082          
083        // zu verwendenden Datenkorb ermitteln
084        final DataBasket db = getBasket();
085          
086        // zu verwendenden Bestand ermitteln
087        final CountingStockImpl cs =
088          (CountingStockImpl)Shop.getTheShop().getStock("Video-Countingstock");
089          
090          
091        //////////////////////////////////////////////////
092        /// Capability-Gate //////////////////////////////
093        //////////////////////////////////////////////////
094          
095          
096        // Formsheet fuer Eingabe der Kundennummer
097        // tif ist final, damit in der anonymen Klasse des FSCC auf Attribute des tif zugegriffen werden kann
098        final TextInputForm tif = new TextInputForm("Customer-ID",
099                                                    "Customer-ID",
100                                                    "");
101          
102        // FormSheetContentCreator fuer Capability-Gate implementieren    
103        tif.addContentCreator(
104          new FormSheetContentCreator()
105          {
106            protected void createFormSheetContent(FormSheet fs)
107            {
108              // "OK"-Button neu definieren
109              fs.getButton(FormSheet.BTNID_OK).
110                setAction(
111                  new sale.Action()
112                  {
113                    public void doAction (SaleProcess p, SalesPoint sp)
114                    {
115                      // Eingabe ueberpruefen
116                      String customerID = tif.getText();
117                      boolean isNotAnInteger = true;
118                                                           
119                      try {
120                        new Integer(customerID);
121                        isNotAnInteger = false;
122                      }
123                      catch(NumberFormatException nfe) {
124                        isNotAnInteger = true;
125                      }
126                                                             
127                      // Eingabe korrekt -> selectionGate
128                      if (!isNotAnInteger && (new Integer(customerID)).intValue() > 0) 
129                      {                                                           
130                        // existiert der Kunde?
131                        try {
132                          customer = new Customer(customerID);
133                          // nein, dann neu anlegen und der Liste reg. Kunden hinzufuegen
134                          VideoMachine.addCustomer(customer);
135                        }
136                        // ja, dann diesen holen
137                        catch (DuplicateKeyException dke) {
138                          customer = VideoMachine.getCustomerByID(customerID);
139                        }
140                        capabilityGate.setNextTransition(toSelectionTransition);
141                      }
142                      // Eingabe falsch -> Kunden informieren und
143                      // Eingabe wiederholen
144                      else {
145                        JOptionPane.showMessageDialog(null,
146                          "CustomerID must be a positive Number!");
147                        capabilityGate.setNextTransition(
148                          new GateChangeTransition(capabilityGate));
149                      }
150                    }
151                  }
152                );
153              // "Cancel"-Button neu definieren
154              fs.getButton(FormSheet.BTNID_CANCEL).
155                setAction( 
156                  new sale.Action()
157                  {
158                    public void doAction (SaleProcess p, SalesPoint sp)
159                    {
160                      // Transition zum Rollback-Gate als naechste Transition setzen
161                      capabilityGate.setNextTransition(
162                        GateChangeTransition.CHANGE_TO_ROLLBACK_GATE);
163                    }
164                  }
165                );
166            }
167          }
168        );
169          
170        // Gate zur Kundennummer-Abfrage mit erstellter TextInputForm anlegen
171        capabilityGate = new UIGate((FormSheet) tif, 
172                                    (MenuSheet) null);
173          
174          
175        //////////////////////////////////////////////////
176        /// Selection-Gate ///////////////////////////////
177        //////////////////////////////////////////////////
178          
179          
180        // Auswahl-Gate anlegen
181        // Das FormSheet wird durch die Transition waehrend der Laufzeit erzeugt
182        selectionGate = new UIGate((FormSheet) null, (MenuSheet) null);
183          
184        // Transition zum Selection Gate
185        toSelectionTransition = 
186          new Transition()
187          {
188            public Gate perform(SaleProcess pOwner, User usr)
189            {
190              // am Gate darzustellendes FormSheet
191              TwoTableFormSheet ttfs = 
192                TwoTableFormSheet.create("Make your selection", // Titel des FormSheets
193                                         cs,                    // Quell-CountingStock
194                                         db,                    // Ziel-DataBasket
195                                         selectionGate,         // Gate, an dem das FormSheet darzustellen ist
196                                         null,                  // Comparator fuer Quelltabelle
197                                         null,                  // Comparator furr Ziel-Tabelle
198                                         false,        // Zeilen mit Anzahl == 0 in der Quelltabelle anzeigen?
199                                         new OfferTED(true),    // TableEntryDescriptor furr Quelltabelle
200                                         null,                  // TableEntryDescriptor furr Zieltabelle
201                                         null                   // Transferstrategie
202                                         );
203                      
204              // FormSheetContentCreator am Auswahl-Gate anmelden    
205              ttfs.addContentCreator(
206                new FormSheetContentCreator()
207                {
208                  protected void createFormSheetContent(FormSheet fs)
209                  {
210                    // "OK"-Button neu definieren
211                    fs.getButton(FormSheet.BTNID_OK).
212                      setAction( 
213                        new sale.Action()
214                        {
215                          public void doAction (SaleProcess p, SalesPoint sp)
216                          {
217                            // Transition zum Bezahlen als naechste Transition setzen
218                            selectionGate.setNextTransition(toPayingTransition);
219                          }
220                        }
221                      );
222                                                   
223                    // "Cancel"-Button neu definieren
224                    fs.getButton(FormSheet.BTNID_CANCEL).
225                      setAction(  
226                        new sale.Action()
227                        {
228                          public void doAction (SaleProcess p, SalesPoint sp)
229                          {
230                            // Transition zum Rollback-Gate als naechste Transition setzen
231                            selectionGate.setNextTransition(
232                              GateChangeTransition.CHANGE_TO_ROLLBACK_GATE);
233                          }
234                        }
235                      );
236                  }
237                }
238              );
239                      
240              // erstelltes FormSheet am zu betretenden Gate setzen
241              selectionGate.setFormSheet(ttfs);
242                     
243              // als naechstes zu betretendes Gate zurueckgeben
244              return selectionGate;
245            }
246          };                        
247          
248          
249        //////////////////////////////////////////////////
250        /// Rent-Gate ////////////////////////////////////
251        //////////////////////////////////////////////////
252          
253          
254        // Gate zum Ausleihen/Bezahlen anlegen
255        // Das FormSheet wird durch die Transition waehrend der Laufzeit erzeugt
256        rentGate = new UIGate(null, null);
257          
258        // Transition zum Gate, an dem zu bezahlen ist
259        toPayingTransition = 
260          new Transition()
261          {
262            public Gate perform(SaleProcess pOwner, User usr)
263            {
264              // Parameter zum Aufsummieren des Datenkorbes festlegen
265              final DataBasketCondition dbc =
266                DataBasketConditionImpl.allStockItemsWithSource(cs);
267              BasketEntryValue bev = BasketEntryValues.ONLY_STOCK_ITEMS;
268              QuoteValue qvSum = new QuoteValue(new IntegerValue(0), new IntegerValue(0));
269                      
270              // ...und Datenkorb damit aufsummieren
271              pOwner.getBasket().sumBasket (dbc, bev, qvSum);
272                      
273              // zu zahlenden Betrag ermitteln
274              toPayValue = (IntegerValue)qvSum.getBid();
275                      
276              // FormSheet für das naechste Gate erstellen
277              FormSheet tif = new TextInputForm("Paying",
278                                                "You have to pay " +
279                                                   myCurrency.toString (toPayValue) + ".",
280                                                myCurrency.toString (toPayValue)
281                                               );
282                      
283              // FormSheetContentCreator zum Einbauen der Buttons anmelden
284              tif.addContentCreator(
285                new FormSheetContentCreator()
286                {
287                  protected void createFormSheetContent(FormSheet fs)
288                  {
289                    // als 'final' markierte Version des FormSheets anlegen
290                    final TextInputForm tifFinal = (TextInputForm)fs;
291                                                   
292                    // der Cancel-Button steht im Weg und wird zunaechst entfernt
293                    fs.removeButton(FormSheet.BTNID_CANCEL);
294                                                    
295                    // "OK"-Button neu definieren
296                    fs.getButton(FormSheet.BTNID_OK).
297                      setAction( 
298                        new sale.Action()
299                        {
300                          public void doAction (SaleProcess p, SalesPoint sp)
301                          {
302                            try {
303                              // eingegebenen Text verarbeiten
304                              paidValue = (IntegerValue)myCurrency.parse(tifFinal.getText());
305                                                                            
306                              // Der Iterator ueber die zu leihenden Videos wird initialisiert
307                              videoIterator = db.iterator(dbc);
308                                                                            
309                              // ...und naechste Transition setzen
310                              rentGate.setNextTransition(toDecisionTransition);
311                            }
312                            catch(ParseException pexc) {
313                              // eingegebener Text konnte nicht verarbeitet werden
314                              // Meldung erzeugen und ausgeben
315                              MsgForm mf = new MsgForm ("Error",
316                                                        "The specified amount does " +
317                                                           "not have an appropriate format.");
318                                                                               
319                              try {
320                                p.getContext().popUpFormSheet (p, mf);
321                              }
322                              catch(InterruptedException iexc) {
323                              }
324                            }
325                          }
326                        }
327                      );
328                                                    
329                    // "Back"-Button einbauen
330                    fs.addButton ("Back", 101, 
331                      new sale.Action()
332                      {
333                        public void doAction (SaleProcess p, SalesPoint sp)
334                        {  
335                          // naechste Transition setzen, fuehrt ohne weitere Aktionen
336                          // zum Auswahl-Gate
337                          rentGate.setNextTransition(
338                            new GateChangeTransition(selectionGate));
339                        }
340                      }
341                    );
342                                                    
343                    // "Cancel"-Button wieder einbauen
344                    fs.addButton ("Cancel", 102, 
345                      new sale.Action()
346                      {
347                        public void doAction (SaleProcess p, SalesPoint sp)
348                        {
349                          // naechste Transition setzen, fuehrt zum Rollback-Gate
350                          rentGate.setNextTransition(
351                            GateChangeTransition.CHANGE_TO_ROLLBACK_GATE);
352                        }
353                      }
354                    );
355                                                    
356                  }
357                }
358              );
359                      
360              // FormSheet am entsprechenden Gate setzen
361              rentGate.setFormSheet(tif);
362               
363              // ...und naechstes zu betretendes Gate zurueckgeben
364              return rentGate;
365            }
366          };
367          
368          
369        //////////////////////////////////////////////////
370        /// Decision-Gate ////////////////////////////////
371        //////////////////////////////////////////////////
372          
373          
374        // Transition zum Entscheidungsgate
375        toDecisionTransition = 
376          new Transition()
377          {
378            public Gate perform (SaleProcess pOwner, User usr)
379            {
380              // ausreichend Geld gegeben?
381              if (paidValue.compareTo (toPayValue) >= 0) 
382              {
383                // zu zahlenden Betrag im Safe der Videomaschine ablegen, wenn
384                // ueberhaupt etwas zu bezahlen ist
385                if (toPayValue.getValue().intValue() > 0)
386                  ((CountingStock)Shop.getTheShop().
387                    getStock("coin slot")).add(CurrencyImpl.PFENNIG_STCK_1,
388                      toPayValue.getValue().intValue(), pOwner.getBasket());
389                         
390                // Genau richtig gezahlt? - payAssessment entsprechend belegen
391                if (paidValue.compareTo(toPayValue) == 0)
392                  payAssessment = 0;
393                else
394                  payAssessment = 1;
395              }                     
396              // nein, zuwenig bezahlt - payAssessment entsprechend belegen
397              else
398                payAssessment = -1;
399                     
400              // Entscheidungsgate als naechstes zu betretendes Gate zurueckgeben
401              return decisionGate;
402            }
403          };
404          
405          
406        // Gate anlegen, das entscheidet, ob Wechselgeld gegeben, zum Bezahlen
407        // oder zum Commit-Gate gegangen wird
408        decisionGate = 
409          new Gate()
410          {
411            public Transition getNextTransition(SaleProcess pOwner, User usr)
412              throws InterruptedException
413            {
414              switch(payAssessment) 
415              {
416                // zuwenig bezahlt - Message anzeigen und zurueck zum Bezahlen
417                case -1:
418                // FehlerFormSheet erzeugen
419                  FormSheet mf = new MsgForm("Error",
420                                             "You have to pay more!",
421                                             false);
422                  // mit Hilfe des ProcessContext das FormSheet anzeigen
423                  pOwner.getContext().popUpFormSheet(pOwner, mf);
424                            
425                  return new GateChangeTransition(rentGate);
426                            
427                // genau richtig bezahlt - Transaktion ausfuehren - zum Commit-Gate
428                case  0:
429                  // Ausleihvorgang ausfuehren
430                  commitTransfer();
431    
432                  return GateChangeTransition.CHANGE_TO_COMMIT_GATE;
433                            
434                // zuviel bezahlt - zur Wechselgeldausgabe
435                case  1:
436                  // Ausleihvorgang ausfuehren
437                  commitTransfer();
438     
439                  return toGetChangeTransition;
440                            
441                // falscher Wert in payAssessment - Fehlermeldung und Abbruch
442                default:
443                  FormSheet mf2 = new MsgForm("Error",
444                                              "Internal error at Decision Gate. " +
445                                              "Will quit process.",
446                                              false);
447                  pOwner.getContext().popUpFormSheet (pOwner, mf2);
448                  return GateChangeTransition.CHANGE_TO_QUIT_GATE;
449              }
450            }
451                   
452            public void commitTransfer()
453            {
454              // aktuelles Datum holen
455              Object date = Shop.getTheShop().getTimer().getTime();
456              while (videoIterator.hasNext())
457              {
458                CountingStockItemDBEntry cassetteItem =
459                  (CountingStockItemDBEntry)videoIterator.next();
460                int number = cassetteItem.count();
461                for (; number > 0; number --)
462                {
463                  // Videos in den Bestand des Kunden uebernehmen
464                  customer.addVideoCassette(new 
465                    CassetteStoringStockItem(cassetteItem.getSecondaryKey(),        date));
466                           
467                  // Vorgang in Logdatei eintragen
468                  try {
469                    Log.getGlobalLog().log(new MyLoggable(cassetteItem.getSecondaryKey(),
470                                                          customer.getCustomerID(), 
471                                                          date));
472                  }
473                  catch (LogNoOutputStreamException lnose) {
474                  }
475                  catch (IOException ioe) {
476                  }
477                }
478              }
479            }
480          };
481          
482          
483        //////////////////////////////////////////////////
484        /// GetChange-Gate ///////////////////////////////
485        //////////////////////////////////////////////////
486          
487          
488        // Gate zum Ausgeben des Wechselgeldes anlegen
489        // Das FormSheet wird durch die Transition waehrend der Laufzeit erzeugt
490        getChangeGate = new UIGate(null, null);
491          
492        // Transition zum Wechselgeld-Gate
493        toGetChangeTransition = 
494          new Transition()
495          {
496            public Gate perform(SaleProcess pOwner, User usr)
497            {
498              // am Gate darzustellendes FormSheet
499              MsgForm mf = new MsgForm("Get Change",
500                                       "You get " +
501                                          myCurrency.toString((IntegerValue)paidValue.subtract(toPayValue)) +
502                                            " Change.");
503                      
504              // ContentCreator zur Neubelegung des "OK"-Buttons hinzufuegen 
505              mf.addContentCreator(
506                new FormSheetContentCreator()
507                {
508                  public void createFormSheetContent(FormSheet fs)
509                  {
510                    // neue Aktion setzen
511                    fs.getButton(FormSheet.BTNID_OK).setAction(
512                      new Action()
513                      {
514                        public void doAction(SaleProcess p, SalesPoint sp)
515                        {
516                          // zum Commit-Gate fuehrende Transition als
517                          // naechste Transition setzen
518                          getChangeGate.setNextTransition(
519                            GateChangeTransition.CHANGE_TO_COMMIT_GATE);
520                        }
521                      }
522                    ); 
523                  }
524                }
525              );
526                      
527              // erstelltes FormSheet am zu betretenden Gate setzen
528              getChangeGate.setFormSheet(mf);
529                     
530              // als naechstes zu betretendes Gate zurueckgeben
531              return getChangeGate;
532            }
533          };      
534      }
535       
536      //// public methods ////////////////////////////////////////////////////////
537       
538      /**
539       * Gibt das Startgate des Prozesses zurück.
540       */
541      public Gate getInitialGate()
542      {  
543        // Automat aufbauen
544        setupMachine();
545          
546        // ...und Capability-Gate als Startgate zurueckgeben
547        return capabilityGate;
548      }
549       
550      /**
551       * Übergibt das Log-Gate. Hier das Stop-Gate, da beim Beenden des
552       * Prozesses kein Log-Eintrag geschrieben werden soll.
553       */
554      public Gate getLogGate()
555      {
556        return getStopGate();
557      }
558    }