GnuCash  4.8a-132-gcdaeb421d+
gncCustomer.c
1 /********************************************************************\
2  * gncCustomer.c -- the Core Customer Interface *
3  * *
4  * This program is free software; you can redistribute it and/or *
5  * modify it under the terms of the GNU General Public License as *
6  * published by the Free Software Foundation; either version 2 of *
7  * the License, or (at your option) any later version. *
8  * *
9  * This program is distributed in the hope that it will be useful, *
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
12  * GNU General Public License for more details. *
13  * *
14  * You should have received a copy of the GNU General Public License*
15  * along with this program; if not, contact: *
16  * *
17  * Free Software Foundation Voice: +1-617-542-5942 *
18  * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
19  * Boston, MA 02110-1301, USA gnu@gnu.org *
20  * *
21 \********************************************************************/
22 
23 /*
24  * Copyright (C) 2001,2002 Derek Atkins
25  * Copyright (C) 2003 Linas Vepstas <linas@linas.org>
26  * Author: Derek Atkins <warlord@MIT.EDU>
27  */
28 
29 #include <config.h>
30 
31 #include <glib.h>
32 #include <string.h>
33 #include <qofinstance-p.h>
34 
35 #include "gnc-commodity.h"
36 
37 #include "gncAddressP.h"
38 #include "gncBillTermP.h"
39 #include "gncInvoice.h"
40 #include "gncBusiness.h"
41 
42 #include "gncCustomer.h"
43 #include "gncCustomerP.h"
44 #include "gncJobP.h"
45 #include "gncTaxTableP.h"
46 
47 static gint cust_qof_event_handler_id = 0;
48 static void cust_handle_qof_events (QofInstance *entity, QofEventId event_type,
49  gpointer user_data, gpointer event_data);
50 
52 {
53  QofInstance inst;
54 
55  /* The following fields are identical to 'vendor' */
56  const char * id;
57  const char * name;
58  const char * notes;
59  GncBillTerm * terms;
60  GncAddress * addr;
61  gnc_commodity * currency;
62  GncTaxTable* taxtable;
63  gboolean taxtable_override;
64  GncTaxIncluded taxincluded;
65  gboolean active;
66  GList * jobs;
67  gnc_numeric * balance; /* cached customer balance, will not be stored */
68 
69  /* The following fields are unique to 'customer' */
70  gnc_numeric credit;
71  gnc_numeric discount;
72  GncAddress * shipaddr;
73 };
74 
76 {
77  QofInstanceClass parent_class;
78 };
79 
80 static QofLogModule log_module = GNC_MOD_BUSINESS;
81 
82 #define _GNC_MOD_NAME GNC_ID_CUSTOMER
83 
84 /* ============================================================== */
85 /* misc inline funcs */
86 
87 static inline void mark_customer (GncCustomer *customer);
88 void mark_customer (GncCustomer *customer)
89 {
90  qof_instance_set_dirty(&customer->inst);
91  qof_event_gen (&customer->inst, QOF_EVENT_MODIFY, NULL);
92 }
93 
94 /* ============================================================== */
95 
96 enum
97 {
98  PROP_0,
99  PROP_NAME,
100  PROP_PDF_DIRNAME,
101  PROP_LAST_POSTED,
102  PROP_PAYMENT_LAST_ACCT,
103 };
104 
105 /* GObject Initialization */
106 G_DEFINE_TYPE(GncCustomer, gnc_customer, QOF_TYPE_INSTANCE);
107 
108 static void
109 gnc_customer_init(GncCustomer* cust)
110 {
111 }
112 
113 static void
114 gnc_customer_dispose(GObject *custp)
115 {
116  G_OBJECT_CLASS(gnc_customer_parent_class)->dispose(custp);
117 }
118 
119 static void
120 gnc_customer_finalize(GObject* custp)
121 {
122  G_OBJECT_CLASS(gnc_customer_parent_class)->finalize(custp);
123 }
124 
125 static void
126 gnc_customer_get_property (GObject *object,
127  guint prop_id,
128  GValue *value,
129  GParamSpec *pspec)
130 {
131  GncCustomer *cust;
132  gchar *key;
133  g_return_if_fail(GNC_IS_CUSTOMER(object));
134 
135  cust = GNC_CUSTOMER(object);
136  switch (prop_id)
137  {
138  case PROP_NAME:
139  g_value_set_string(value, cust->name);
140  break;
141  case PROP_PDF_DIRNAME:
142  qof_instance_get_kvp (QOF_INSTANCE (cust), value, 1, OWNER_EXPORT_PDF_DIRNAME);
143  break;
144  case PROP_LAST_POSTED:
145  qof_instance_get_kvp (QOF_INSTANCE (cust), value, 1, LAST_POSTED_TO_ACCT);
146  break;
147  case PROP_PAYMENT_LAST_ACCT:
148  qof_instance_get_kvp (QOF_INSTANCE (cust), value, 2, GNC_PAYMENT, GNC_LAST_ACCOUNT);
149  break;
150  default:
151  G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
152  break;
153  }
154 }
155 
156 static void
157 gnc_customer_set_property (GObject *object,
158  guint prop_id,
159  const GValue *value,
160  GParamSpec *pspec)
161 {
162  GncCustomer *cust;
163  gchar *key;
164 
165  g_return_if_fail(GNC_IS_CUSTOMER(object));
166 
167  cust = GNC_CUSTOMER(object);
168  g_assert (qof_instance_get_editlevel(cust));
169 
170  switch (prop_id)
171  {
172  case PROP_NAME:
173  gncCustomerSetName(cust, g_value_get_string(value));
174  break;
175  case PROP_PDF_DIRNAME:
176  qof_instance_set_kvp (QOF_INSTANCE (cust), value, 1, OWNER_EXPORT_PDF_DIRNAME);
177  break;
178  case PROP_LAST_POSTED:
179  qof_instance_set_kvp (QOF_INSTANCE (cust), value, 1, LAST_POSTED_TO_ACCT);
180  break;
181  case PROP_PAYMENT_LAST_ACCT:
182  qof_instance_set_kvp (QOF_INSTANCE (cust), value, 2, GNC_PAYMENT, GNC_LAST_ACCOUNT);
183  break;
184  default:
185  G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
186  break;
187  }
188 }
189 
191 static gchar*
192 impl_get_display_name(const QofInstance* inst)
193 {
194  GncCustomer* cust;
195 
196  g_return_val_if_fail(inst != NULL, FALSE);
197  g_return_val_if_fail(GNC_IS_CUSTOMER(inst), FALSE);
198 
199  cust = GNC_CUSTOMER(inst);
200  /* XXX internationalization of "Customer" */
201  return g_strdup_printf("Customer %s", cust->name);
202 }
203 
205 static gboolean
206 impl_refers_to_object(const QofInstance* inst, const QofInstance* ref)
207 {
208  GncCustomer* cust;
209 
210  g_return_val_if_fail(inst != NULL, FALSE);
211  g_return_val_if_fail(GNC_IS_CUSTOMER(inst), FALSE);
212 
213  cust = GNC_CUSTOMER(inst);
214 
215  if (GNC_IS_BILLTERM(ref))
216  {
217  return (cust->terms == GNC_BILLTERM(ref));
218  }
219  else if (GNC_IS_TAXTABLE(ref))
220  {
221  return (cust->taxtable == GNC_TAXTABLE(ref));
222  }
223 
224  return FALSE;
225 }
226 
233 static GList*
234 impl_get_typed_referring_object_list(const QofInstance* inst, const QofInstance* ref)
235 {
236  if (!GNC_IS_BILLTERM(ref) && !GNC_IS_TAXTABLE(ref))
237  {
238  return NULL;
239  }
240 
242 }
243 
244 static void
245 gnc_customer_class_init (GncCustomerClass *klass)
246 {
247  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
248  QofInstanceClass* qof_class = QOF_INSTANCE_CLASS(klass);
249 
250  gobject_class->dispose = gnc_customer_dispose;
251  gobject_class->finalize = gnc_customer_finalize;
252  gobject_class->set_property = gnc_customer_set_property;
253  gobject_class->get_property = gnc_customer_get_property;
254 
255  qof_class->get_display_name = impl_get_display_name;
256  qof_class->refers_to_object = impl_refers_to_object;
257  qof_class->get_typed_referring_object_list = impl_get_typed_referring_object_list;
258 
259  g_object_class_install_property
260  (gobject_class,
261  PROP_NAME,
262  g_param_spec_string ("name",
263  "Customer Name",
264  "The customer is an arbitrary string "
265  "assigned by the user which provides the "
266  "customer name.",
267  NULL,
268  G_PARAM_READWRITE));
269 
270  g_object_class_install_property
271  (gobject_class,
272  PROP_PDF_DIRNAME,
273  g_param_spec_string ("export-pdf-dir",
274  "Export PDF Directory Name",
275  "A subdirectory for exporting PDF reports which is "
276  "appended to the target directory when writing them "
277  "out. It is retrieved from preferences and stored on "
278  "each 'Owner' object which prints items after "
279  "printing.",
280  NULL,
281  G_PARAM_READWRITE));
282 
283  g_object_class_install_property(
284  gobject_class,
285  PROP_LAST_POSTED,
286  g_param_spec_boxed("invoice-last-posted-account",
287  "Invoice Last Posted Account",
288  "The last account to which an invoice belonging to "
289  "this owner was posted.",
290  GNC_TYPE_GUID,
291  G_PARAM_READWRITE));
292 
293  g_object_class_install_property(
294  gobject_class,
295  PROP_PAYMENT_LAST_ACCT,
296  g_param_spec_boxed("payment-last-account",
297  "Payment Last Account",
298  "The last account to which an payment belonging to "
299  "this owner was posted.",
300  GNC_TYPE_GUID,
301  G_PARAM_READWRITE));
302 }
303 
304 /* Create/Destroy Functions */
305 GncCustomer *gncCustomerCreate (QofBook *book)
306 {
307  GncCustomer *cust;
308 
309  if (!book) return NULL;
310 
311  cust = g_object_new (GNC_TYPE_CUSTOMER, NULL);
312  qof_instance_init_data (&cust->inst, _GNC_MOD_NAME, book);
313 
314  cust->id = CACHE_INSERT ("");
315  cust->name = CACHE_INSERT ("");
316  cust->notes = CACHE_INSERT ("");
317  cust->addr = gncAddressCreate (book, &cust->inst);
318  cust->taxincluded = GNC_TAXINCLUDED_USEGLOBAL;
319  cust->active = TRUE;
320  cust->jobs = NULL;
321  cust->balance = NULL;
322 
323  cust->discount = gnc_numeric_zero();
324  cust->credit = gnc_numeric_zero();
325  cust->shipaddr = gncAddressCreate (book, &cust->inst);
326 
327  if (cust_qof_event_handler_id == 0)
328  cust_qof_event_handler_id = qof_event_register_handler (cust_handle_qof_events, NULL);
329 
330  qof_event_gen (&cust->inst, QOF_EVENT_CREATE, NULL);
331 
332  return cust;
333 }
334 
335 void gncCustomerDestroy (GncCustomer *cust)
336 {
337  if (!cust) return;
338  qof_instance_set_destroying(cust, TRUE);
339  qof_instance_set_dirty (&cust->inst);
340  gncCustomerCommitEdit (cust);
341 }
342 
343 static void gncCustomerFree (GncCustomer *cust)
344 {
345  if (!cust) return;
346 
347  qof_event_gen (&cust->inst, QOF_EVENT_DESTROY, NULL);
348 
349  CACHE_REMOVE (cust->id);
350  CACHE_REMOVE (cust->name);
351  CACHE_REMOVE (cust->notes);
352  gncAddressBeginEdit (cust->addr);
353  gncAddressDestroy (cust->addr);
354  gncAddressBeginEdit (cust->shipaddr);
355  gncAddressDestroy (cust->shipaddr);
356  g_list_free (cust->jobs);
357  g_free (cust->balance);
358 
359  if (cust->terms)
360  gncBillTermDecRef (cust->terms);
361  if (cust->taxtable)
362  {
363  gncTaxTableDecRef (cust->taxtable);
364  }
365 
366  /* qof_instance_release (&cust->inst); */
367  g_object_unref (cust);
368 }
369 
370 /* ============================================================== */
371 /* Set Functions */
372 
373 #define SET_STR(obj, member, str) { \
374  if (!g_strcmp0 (member, str)) return; \
375  gncCustomerBeginEdit (obj); \
376  CACHE_REPLACE(member, str); \
377  }
378 
379 void gncCustomerSetID (GncCustomer *cust, const char *id)
380 {
381  if (!cust) return;
382  if (!id) return;
383  SET_STR(cust, cust->id, id);
384  mark_customer (cust);
385  gncCustomerCommitEdit (cust);
386 }
387 
388 void gncCustomerSetName (GncCustomer *cust, const char *name)
389 {
390  if (!cust) return;
391  if (!name) return;
392  SET_STR(cust, cust->name, name);
393  mark_customer (cust);
394  gncCustomerCommitEdit (cust);
395 }
396 
397 void gncCustomerSetNotes (GncCustomer *cust, const char *notes)
398 {
399  if (!cust) return;
400  if (!notes) return;
401  SET_STR(cust, cust->notes, notes);
402  mark_customer (cust);
403  gncCustomerCommitEdit (cust);
404 }
405 
406 void gncCustomerSetTerms (GncCustomer *cust, GncBillTerm *terms)
407 {
408  if (!cust) return;
409  if (cust->terms == terms) return;
410 
411  gncCustomerBeginEdit (cust);
412  if (cust->terms)
413  gncBillTermDecRef (cust->terms);
414  cust->terms = terms;
415  if (cust->terms)
416  gncBillTermIncRef (cust->terms);
417  mark_customer (cust);
418  gncCustomerCommitEdit (cust);
419 }
420 
421 void gncCustomerSetTaxIncluded (GncCustomer *cust, GncTaxIncluded taxincl)
422 {
423  if (!cust) return;
424  if (taxincl == cust->taxincluded) return;
425  gncCustomerBeginEdit (cust);
426  cust->taxincluded = taxincl;
427  mark_customer (cust);
428  gncCustomerCommitEdit (cust);
429 }
430 
431 void gncCustomerSetActive (GncCustomer *cust, gboolean active)
432 {
433  if (!cust) return;
434  if (active == cust->active) return;
435  gncCustomerBeginEdit (cust);
436  cust->active = active;
437  mark_customer (cust);
438  gncCustomerCommitEdit (cust);
439 }
440 
441 void gncCustomerSetDiscount (GncCustomer *cust, gnc_numeric discount)
442 {
443  if (!cust) return;
444  if (gnc_numeric_equal (discount, cust->discount)) return;
445  gncCustomerBeginEdit (cust);
446  cust->discount = discount;
447  mark_customer (cust);
448  gncCustomerCommitEdit (cust);
449 }
450 
451 void gncCustomerSetCredit (GncCustomer *cust, gnc_numeric credit)
452 {
453  if (!cust) return;
454  if (gnc_numeric_equal (credit, cust->credit)) return;
455  gncCustomerBeginEdit (cust);
456  cust->credit = credit;
457  mark_customer (cust);
458  gncCustomerCommitEdit (cust);
459 }
460 
461 void gncCustomerSetCurrency (GncCustomer *cust, gnc_commodity *currency)
462 {
463  if (!cust || !currency) return;
464  if (cust->currency && gnc_commodity_equal (cust->currency, currency)) return;
465  gncCustomerBeginEdit (cust);
466  cust->currency = currency;
467  mark_customer (cust);
468  gncCustomerCommitEdit (cust);
469 }
470 
471 void gncCustomerSetTaxTableOverride (GncCustomer *customer, gboolean override)
472 {
473  if (!customer) return;
474  if (customer->taxtable_override == override) return;
475  gncCustomerBeginEdit (customer);
476  customer->taxtable_override = override;
477  mark_customer (customer);
478  gncCustomerCommitEdit (customer);
479 }
480 
481 void gncCustomerSetTaxTable (GncCustomer *customer, GncTaxTable *table)
482 {
483  if (!customer) return;
484  if (customer->taxtable == table) return;
485 
486  gncCustomerBeginEdit (customer);
487  if (customer->taxtable)
488  gncTaxTableDecRef (customer->taxtable);
489  if (table)
490  gncTaxTableIncRef (table);
491  customer->taxtable = table;
492  mark_customer (customer);
493  gncCustomerCommitEdit (customer);
494 }
495 
496 /* Note that JobList changes do not affect the "dirtiness" of the customer */
497 void gncCustomerAddJob (GncCustomer *cust, GncJob *job)
498 {
499  if (!cust) return;
500  if (!job) return;
501 
502  if (g_list_index(cust->jobs, job) == -1)
503  cust->jobs = g_list_insert_sorted (cust->jobs, job,
504  (GCompareFunc)gncJobCompare);
505 
506  qof_event_gen (&cust->inst, QOF_EVENT_MODIFY, NULL);
507 }
508 
509 void gncCustomerRemoveJob (GncCustomer *cust, GncJob *job)
510 {
511  GList *node;
512 
513  if (!cust) return;
514  if (!job) return;
515 
516  node = g_list_find (cust->jobs, job);
517  if (!node)
518  {
519  /* PERR ("split not in account"); */
520  }
521  else
522  {
523  cust->jobs = g_list_remove_link (cust->jobs, node);
524  g_list_free_1 (node);
525  }
526  qof_event_gen (&cust->inst, QOF_EVENT_MODIFY, NULL);
527 }
528 
529 void gncCustomerBeginEdit (GncCustomer *cust)
530 {
531  qof_begin_edit (&cust->inst);
532 }
533 
534 static void gncCustomerOnError (QofInstance *inst, QofBackendError errcode)
535 {
536  PERR("Customer QofBackend Failure: %d", errcode);
537  gnc_engine_signal_commit_error( errcode );
538 }
539 
540 static void gncCustomerOnDone (QofInstance *inst)
541 {
542  GncCustomer *cust = (GncCustomer *) inst;
543  gncAddressClearDirty (cust->addr);
544  gncAddressClearDirty (cust->shipaddr);
545 }
546 
547 static void cust_free (QofInstance *inst)
548 {
549  GncCustomer *cust = (GncCustomer *) inst;
550  gncCustomerFree (cust);
551 }
552 
553 void gncCustomerCommitEdit (GncCustomer *cust)
554 {
555  if (!qof_commit_edit (QOF_INSTANCE(cust))) return;
556  qof_commit_edit_part2 (&cust->inst, gncCustomerOnError,
557  gncCustomerOnDone, cust_free);
558 }
559 
560 /* ============================================================== */
561 /* Get Functions */
562 
563 const char * gncCustomerGetID (const GncCustomer *cust)
564 {
565  if (!cust) return NULL;
566  return cust->id;
567 }
568 
569 const char * gncCustomerGetName (const GncCustomer *cust)
570 {
571  if (!cust) return NULL;
572  return cust->name;
573 }
574 
575 GncAddress * gncCustomerGetAddr (const GncCustomer *cust)
576 {
577  if (!cust) return NULL;
578  return cust->addr;
579 }
580 
581 static void
582 qofCustomerSetAddr (GncCustomer *cust, QofInstance *addr_ent)
583 {
584  GncAddress *addr;
585 
586  if (!cust || !addr_ent)
587  {
588  return;
589  }
590  addr = (GncAddress*)addr_ent;
591  if (addr == cust->addr)
592  {
593  return;
594  }
595  if (cust->addr != NULL)
596  {
597  gncAddressBeginEdit(cust->addr);
598  gncAddressDestroy(cust->addr);
599  }
600  gncCustomerBeginEdit(cust);
601  cust->addr = addr;
602  gncCustomerCommitEdit(cust);
603 }
604 
605 static void
606 qofCustomerSetShipAddr (GncCustomer *cust, QofInstance *ship_addr_ent)
607 {
608  GncAddress *ship_addr;
609 
610  if (!cust || !ship_addr_ent)
611  {
612  return;
613  }
614  ship_addr = (GncAddress*)ship_addr_ent;
615  if (ship_addr == cust->shipaddr)
616  {
617  return;
618  }
619  if (cust->shipaddr != NULL)
620  {
621  gncAddressBeginEdit(cust->shipaddr);
622  gncAddressDestroy(cust->shipaddr);
623  }
624  gncCustomerBeginEdit(cust);
625  cust->shipaddr = ship_addr;
626  gncCustomerCommitEdit(cust);
627 }
628 
629 GncAddress * gncCustomerGetShipAddr (const GncCustomer *cust)
630 {
631  if (!cust) return NULL;
632  return cust->shipaddr;
633 }
634 
635 const char * gncCustomerGetNotes (const GncCustomer *cust)
636 {
637  if (!cust) return NULL;
638  return cust->notes;
639 }
640 
641 GncBillTerm * gncCustomerGetTerms (const GncCustomer *cust)
642 {
643  if (!cust) return NULL;
644  return cust->terms;
645 }
646 
647 GncTaxIncluded gncCustomerGetTaxIncluded (const GncCustomer *cust)
648 {
649  if (!cust) return GNC_TAXINCLUDED_USEGLOBAL;
650  return cust->taxincluded;
651 }
652 
653 gnc_commodity * gncCustomerGetCurrency (const GncCustomer *cust)
654 {
655  if (!cust) return NULL;
656  return cust->currency;
657 }
658 
659 gboolean gncCustomerGetActive (const GncCustomer *cust)
660 {
661  if (!cust) return FALSE;
662  return cust->active;
663 }
664 
665 gnc_numeric gncCustomerGetDiscount (const GncCustomer *cust)
666 {
667  if (!cust) return gnc_numeric_zero();
668  return cust->discount;
669 }
670 
671 gnc_numeric gncCustomerGetCredit (const GncCustomer *cust)
672 {
673  if (!cust) return gnc_numeric_zero();
674  return cust->credit;
675 }
676 
677 gboolean gncCustomerGetTaxTableOverride (const GncCustomer *customer)
678 {
679  if (!customer) return FALSE;
680  return customer->taxtable_override;
681 }
682 
683 GncTaxTable* gncCustomerGetTaxTable (const GncCustomer *customer)
684 {
685  if (!customer) return NULL;
686  return customer->taxtable;
687 }
688 
689 GList * gncCustomerGetJoblist (const GncCustomer *cust, gboolean show_all)
690 {
691  if (!cust) return NULL;
692 
693  if (show_all)
694  {
695  return (g_list_copy (cust->jobs));
696  }
697  else
698  {
699  GList *list = NULL, *iterator;
700  for (iterator = cust->jobs; iterator; iterator = iterator->next)
701  {
702  GncJob *j = iterator->data;
703  if (gncJobGetActive (j))
704  list = g_list_prepend (list, j);
705  }
706  return g_list_reverse (list);
707  }
708 }
709 
710 gboolean gncCustomerIsDirty (GncCustomer *cust)
711 {
712  if (!cust) return FALSE;
713  return (qof_instance_is_dirty(&cust->inst) ||
714  gncAddressIsDirty (cust->addr) ||
715  gncAddressIsDirty (cust->shipaddr));
716 }
717 
718 /* Other functions */
719 
720 int gncCustomerCompare (const GncCustomer *a, const GncCustomer *b)
721 {
722  if (!a && !b) return 0;
723  if (!a && b) return 1;
724  if (a && !b) return -1;
725 
726  return(strcmp(a->name, b->name));
727 }
728 
729 gboolean
731 {
732  if (a == NULL && b == NULL) return TRUE;
733  if (a == NULL || b == NULL) return FALSE;
734 
735  g_return_val_if_fail(GNC_IS_CUSTOMER(a), FALSE);
736  g_return_val_if_fail(GNC_IS_CUSTOMER(b), FALSE);
737 
738  if (g_strcmp0(a->id, b->id) != 0)
739  {
740  PWARN("IDs differ: %s vs %s", a->id, b->id);
741  return FALSE;
742  }
743 
744  if (g_strcmp0(a->name, b->name) != 0)
745  {
746  PWARN("Names differ: %s vs %s", a->name, b->name);
747  return FALSE;
748  }
749 
750  if (g_strcmp0(a->notes, b->notes) != 0)
751  {
752  PWARN("Notes differ: %s vs %s", a->notes, b->notes);
753  return FALSE;
754  }
755 
756  if (!gncBillTermEqual(a->terms, b->terms))
757  {
758  PWARN("Bill terms differ");
759  return FALSE;
760  }
761 
762  if (!gnc_commodity_equal(a->currency, b->currency))
763  {
764  PWARN("currencies differ");
765  return FALSE;
766  }
767 
768  if (!gncTaxTableEqual(a->taxtable, b->taxtable))
769  {
770  PWARN("tax tables differ");
771  return FALSE;
772  }
773 
774  if (a->taxtable_override != b->taxtable_override)
775  {
776  PWARN("Tax table override flags differ");
777  return FALSE;
778  }
779 
780  if (a->taxincluded != b->taxincluded)
781  {
782  PWARN("Tax included flags differ");
783  return FALSE;
784  }
785 
786  if (a->active != b->active)
787  {
788  PWARN("Active flags differ");
789  return FALSE;
790  }
791 
792  if (!gncAddressEqual(a->addr, b->addr))
793  {
794  PWARN("addresses differ");
795  return FALSE;
796  }
797  if (!gncAddressEqual(a->shipaddr, b->shipaddr))
798  {
799  PWARN("addresses differ");
800  return FALSE;
801  }
802 
803  if (!gnc_numeric_equal(a->credit, b->credit))
804  {
805  PWARN("Credit amounts differ");
806  return FALSE;
807  }
808 
809  if (!gnc_numeric_equal(a->discount, b->discount))
810  {
811  PWARN("Discount amounts differ");
812  return FALSE;
813  }
814 
815  /* FIXME: Need to check jobs list
816  GList * jobs;
817  */
818 
819  return TRUE;
820 }
821 
834 static void
835 cust_handle_qof_events (QofInstance *entity, QofEventId event_type,
836  gpointer user_data, gpointer event_data)
837 {
838  /* Handle address change events */
839  if ((GNC_IS_ADDRESS (entity) &&
840  (event_type & QOF_EVENT_MODIFY) != 0))
841  {
842  if (GNC_IS_CUSTOMER (event_data))
843  {
844  GncCustomer* cust = GNC_CUSTOMER (event_data);
845  gncCustomerBeginEdit (cust);
846  mark_customer (cust);
847  gncCustomerCommitEdit (cust);
848  }
849  return;
850  }
851 
852  /* Handle lot change events */
853  if (GNC_IS_LOT (entity))
854  {
855  GNCLot *lot = GNC_LOT (entity);
856  GncOwner lot_owner;
857  const GncOwner *end_owner = NULL;
858  GncInvoice *invoice = gncInvoiceGetInvoiceFromLot (lot);
859 
860  /* Determine the owner associated with the lot */
861  if (invoice)
862  /* Invoice lots */
863  end_owner = gncOwnerGetEndOwner (gncInvoiceGetOwner (invoice));
864  else if (gncOwnerGetOwnerFromLot (lot, &lot_owner))
865  /* Pre-payment lots */
866  end_owner = gncOwnerGetEndOwner (&lot_owner);
867 
868  if (gncOwnerGetType (end_owner) == GNC_OWNER_CUSTOMER)
869  {
870  /* Clear the cached balance */
871  GncCustomer* cust = gncOwnerGetCustomer (end_owner);
872  g_free (cust->balance);
873  cust->balance = NULL;
874  }
875  return;
876  }
877 }
878 
879 /* ============================================================== */
880 /* Package-Private functions */
881 static const char * _gncCustomerPrintable (gpointer item)
882 {
883 // GncCustomer *c = item;
884  if (!item) return "failed";
885  return gncCustomerGetName((GncCustomer*)item);
886 }
887 
888 static void
889 destroy_customer_on_book_close(QofInstance *ent, gpointer data)
890 {
891  GncCustomer* c = GNC_CUSTOMER(ent);
892 
893  gncCustomerBeginEdit(c);
894  gncCustomerDestroy(c);
895 }
896 
901 static void
902 gnc_customer_book_end(QofBook* book)
903 {
904  QofCollection *col;
905 
906  col = qof_book_get_collection(book, GNC_ID_CUSTOMER);
907  qof_collection_foreach(col, destroy_customer_on_book_close, NULL);
908 }
909 
910 static QofObject gncCustomerDesc =
911 {
912  DI(.interface_version = ) QOF_OBJECT_VERSION,
913  DI(.e_type = ) _GNC_MOD_NAME,
914  DI(.type_label = ) "Customer",
915  DI(.create = ) (gpointer)gncCustomerCreate,
916  DI(.book_begin = ) NULL,
917  DI(.book_end = ) gnc_customer_book_end,
918  DI(.is_dirty = ) qof_collection_is_dirty,
919  DI(.mark_clean = ) qof_collection_mark_clean,
920  DI(.foreach = ) qof_collection_foreach,
921  DI(.printable = ) (const char * (*)(gpointer))gncCustomerGetName,
922  DI(.version_cmp = ) (int (*)(gpointer, gpointer)) qof_instance_version_cmp,
923 };
924 
925 gboolean gncCustomerRegister (void)
926 {
927  static QofParam params[] =
928  {
929  { CUSTOMER_ID, QOF_TYPE_STRING, (QofAccessFunc)gncCustomerGetID, (QofSetterFunc)gncCustomerSetID },
930  { CUSTOMER_NAME, QOF_TYPE_STRING, (QofAccessFunc)gncCustomerGetName, (QofSetterFunc)gncCustomerSetName },
931  { CUSTOMER_NOTES, QOF_TYPE_STRING, (QofAccessFunc)gncCustomerGetNotes, (QofSetterFunc)gncCustomerSetNotes },
932  {
933  CUSTOMER_DISCOUNT, QOF_TYPE_NUMERIC, (QofAccessFunc)gncCustomerGetDiscount,
934  (QofSetterFunc)gncCustomerSetDiscount
935  },
936  {
937  CUSTOMER_CREDIT, QOF_TYPE_NUMERIC, (QofAccessFunc)gncCustomerGetCredit,
938  (QofSetterFunc)gncCustomerSetCredit
939  },
940  { CUSTOMER_ADDR, GNC_ID_ADDRESS, (QofAccessFunc)gncCustomerGetAddr, (QofSetterFunc)qofCustomerSetAddr },
941  { CUSTOMER_SHIPADDR, GNC_ID_ADDRESS, (QofAccessFunc)gncCustomerGetShipAddr, (QofSetterFunc)qofCustomerSetShipAddr },
942  {
943  CUSTOMER_TT_OVER, QOF_TYPE_BOOLEAN, (QofAccessFunc)gncCustomerGetTaxTableOverride,
944  (QofSetterFunc)gncCustomerSetTaxTableOverride
945  },
946  { CUSTOMER_TERMS, GNC_ID_BILLTERM, (QofAccessFunc)gncCustomerGetTerms, (QofSetterFunc)gncCustomerSetTerms },
947  { QOF_PARAM_ACTIVE, QOF_TYPE_BOOLEAN, (QofAccessFunc)gncCustomerGetActive, (QofSetterFunc)gncCustomerSetActive },
948  { QOF_PARAM_BOOK, QOF_ID_BOOK, (QofAccessFunc)qof_instance_get_book, NULL },
949  { QOF_PARAM_GUID, QOF_TYPE_GUID, (QofAccessFunc)qof_instance_get_guid, NULL },
950  { NULL },
951  };
952 
953  if (!qof_choice_add_class(GNC_ID_INVOICE, GNC_ID_CUSTOMER, INVOICE_OWNER))
954  {
955  return FALSE;
956  }
957  if (!qof_choice_add_class(GNC_ID_JOB, GNC_ID_CUSTOMER, JOB_OWNER))
958  {
959  return FALSE;
960  }
961  qof_class_register (_GNC_MOD_NAME, (QofSortFunc)gncCustomerCompare, params);
962  if (!qof_choice_create(GNC_ID_CUSTOMER))
963  {
964  return FALSE;
965  }
966  /* temp */
967  _gncCustomerPrintable(NULL);
968  return qof_object_register (&gncCustomerDesc);
969 }
970 
971 gchar *gncCustomerNextID (QofBook *book)
972 {
973  return qof_book_increment_and_format_counter (book, _GNC_MOD_NAME);
974 }
975 
976 const gnc_numeric*
977 gncCustomerGetCachedBalance (GncCustomer *cust)
978 {
979  return cust->balance;
980 }
981 
982 void gncCustomerSetCachedBalance (GncCustomer *cust, const gnc_numeric *new_bal)
983 {
984  if (!new_bal)
985  {
986  if (cust->balance)
987  {
988  g_free (cust->balance);
989  cust->balance = NULL;
990  }
991  return;
992  }
993 
994  if (!cust->balance)
995  cust->balance = g_new0 (gnc_numeric, 1);
996 
997  *cust->balance = *new_bal;
998 }
int qof_instance_version_cmp(const QofInstance *left, const QofInstance *right)
Compare two instances, based on their last update times.
Core Customer Interface.
gboolean gnc_numeric_equal(gnc_numeric a, gnc_numeric b)
Equivalence predicate: Returns TRUE (1) if a and b represent the same number.
const GncGUID * qof_instance_get_guid(gconstpointer inst)
Return the GncGUID of this instance.
void qof_instance_set_kvp(QofInstance *, GValue const *value, unsigned count,...)
Sets a KVP slot to a value from a GValue.
#define qof_instance_is_dirty
Return value of is_dirty flag.
Definition: qofinstance.h:162
QofBook * qof_instance_get_book(gconstpointer inst)
Return the book pointer.
gboolean qof_collection_is_dirty(const QofCollection *col)
Return value of &#39;dirty&#39; flag on collection.
Definition: qofid.cpp:257
GncTaxIncluded
How to interpret the TaxIncluded.
Definition: gncTaxTable.h:74
QofBackendError
The errors that can be reported to the GUI & other front-end users.
Definition: qofbackend.h:57
gchar * qof_book_increment_and_format_counter(QofBook *book, const char *counter_name)
This will increment the named counter for this book and format it.
Definition: qofbook.cpp:703
GList * qof_instance_get_referring_object_list_from_collection(const QofCollection *coll, const QofInstance *ref)
Returns a list of objects from the collection which refer to the specific object. ...
void qof_class_register(QofIdTypeConst obj_name, QofSortFunc default_sort_function, const QofParam *params)
This function registers a new object class with the Qof subsystem.
Definition: qofclass.cpp:86
gboolean gnc_commodity_equal(const gnc_commodity *a, const gnc_commodity *b)
This routine returns TRUE if the two commodities are equal.
QofCollection * qof_instance_get_collection(gconstpointer ptr)
Return the collection this instance belongs to.
int(* QofSortFunc)(gconstpointer, gconstpointer)
This function is the default sort function for a particular object type.
Definition: qofclass.h:222
#define QOF_OBJECT_VERSION
Defines the version of the core object object registration interface.
Definition: qofobject.h:64
gboolean qof_commit_edit(QofInstance *inst)
commit_edit helpers
#define PERR(format, args...)
Log a serious error.
Definition: qoflog.h:244
#define QOF_PARAM_BOOK
"Known" Object Parameters – all objects must support these
Definition: qofquery.h:109
use the global setting
Definition: gncTaxTable.h:78
void qof_collection_foreach(const QofCollection *col, QofInstanceForeachCB cb_func, gpointer user_data)
Call the callback for each entity in the collection.
Definition: qofid.cpp:323
void(* QofSetterFunc)(gpointer, gpointer)
The QofSetterFunc defines an function pointer for parameter setters.
Definition: qofclass.h:184
void qof_instance_get_kvp(QofInstance *, GValue *value, unsigned count,...)
Retrieves the contents of a KVP slot into a provided GValue.
gboolean gncBillTermEqual(const GncBillTerm *a, const GncBillTerm *b)
Check if all internal fields of a and b match.
Definition: gncBillTerm.c:643
gint qof_event_register_handler(QofEventHandler handler, gpointer user_data)
Register a handler for events.
Definition: qofevent.cpp:73
#define PWARN(format, args...)
Log a warning.
Definition: qoflog.h:250
void qof_instance_init_data(QofInstance *inst, QofIdType type, QofBook *book)
Initialise the settings associated with an instance.
gboolean qof_begin_edit(QofInstance *inst)
begin_edit
gint QofEventId
Define the type of events allowed.
Definition: qofevent.h:45
gboolean qof_choice_create(char *type)
Set an object as using QOF_TYPE_CHOICE.
Definition: qofchoice.cpp:67
– Business Helper Functions
gboolean qof_commit_edit_part2(QofInstance *inst, void(*on_error)(QofInstance *, QofBackendError), void(*on_done)(QofInstance *), void(*on_free)(QofInstance *))
part2 – deal with the backend
gboolean gncOwnerGetOwnerFromLot(GNCLot *lot, GncOwner *owner)
Get the owner from the lot.
Definition: gncOwner.c:637
gpointer(* QofAccessFunc)(gpointer object, const QofParam *param)
The QofAccessFunc defines an arbitrary function pointer for access functions.
Definition: qofclass.h:177
void qof_collection_mark_clean(QofCollection *)
reset value of dirty flag
Definition: qofid.cpp:263
gboolean gncAddressEqual(const GncAddress *a, const GncAddress *b)
Deeply compare two addresses.
Definition: gncAddress.c:577
credit, discount and shipaddr are unique to GncCustomer id, name, notes, terms, addr, currency, taxtable, taxtable_override taxincluded, active and jobs are identical to ::GncVendor.
GncOwnerType gncOwnerGetType(const GncOwner *owner)
Returns the GncOwnerType of this owner.
Definition: gncOwner.c:201
const GncOwner * gncOwnerGetEndOwner(const GncOwner *owner)
Get the "parent" Owner or GncGUID thereof.
Definition: gncOwner.c:573
GncInvoice * gncInvoiceGetInvoiceFromLot(GNCLot *lot)
Given a LOT, find and return the Invoice attached to the lot.
Definition: gncInvoice.c:1301
Business Invoice Interface.
gboolean gncCustomerEqual(const GncCustomer *a, const GncCustomer *b)
Test support function, used in gets-dbi-business-stuff.c.
Definition: gncCustomer.c:730
gboolean qof_choice_add_class(const char *select, char *option, char *param_name)
Add the choices for this parameter to the object.
Definition: qofchoice.cpp:78
GncCustomer * gncOwnerGetCustomer(const GncOwner *owner)
If the given owner is of type GNC_OWNER_CUSTOMER, returns the pointer to the customer object...
Definition: gncOwner.c:370
QofCollection * qof_book_get_collection(const QofBook *book, QofIdType entity_type)
Return The table of entities of the given type.
Definition: qofbook.cpp:604
gboolean qof_object_register(const QofObject *object)
Register new types of object objects.
Definition: qofobject.cpp:317
void qof_event_gen(QofInstance *entity, QofEventId event_id, gpointer event_data)
Invoke all registered event handlers using the given arguments.
Definition: qofevent.cpp:231
Commodity handling public routines.
modtime is the internal date of the last modtime See src/doc/business.txt for an explanation of the f...