GnuCash  5.6-150-g038405b370+
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  g_return_if_fail(GNC_IS_CUSTOMER(object));
133 
134  cust = GNC_CUSTOMER(object);
135  switch (prop_id)
136  {
137  case PROP_NAME:
138  g_value_set_string(value, cust->name);
139  break;
140  case PROP_PDF_DIRNAME:
141  qof_instance_get_kvp (QOF_INSTANCE (cust), value, 1, OWNER_EXPORT_PDF_DIRNAME);
142  break;
143  case PROP_LAST_POSTED:
144  qof_instance_get_kvp (QOF_INSTANCE (cust), value, 1, LAST_POSTED_TO_ACCT);
145  break;
146  case PROP_PAYMENT_LAST_ACCT:
147  qof_instance_get_kvp (QOF_INSTANCE (cust), value, 2, GNC_PAYMENT, GNC_LAST_ACCOUNT);
148  break;
149  default:
150  G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
151  break;
152  }
153 }
154 
155 static void
156 gnc_customer_set_property (GObject *object,
157  guint prop_id,
158  const GValue *value,
159  GParamSpec *pspec)
160 {
161  GncCustomer *cust;
162 
163  g_return_if_fail(GNC_IS_CUSTOMER(object));
164 
165  cust = GNC_CUSTOMER(object);
166  g_assert (qof_instance_get_editlevel(cust));
167 
168  switch (prop_id)
169  {
170  case PROP_NAME:
171  gncCustomerSetName(cust, g_value_get_string(value));
172  break;
173  case PROP_PDF_DIRNAME:
174  qof_instance_set_kvp (QOF_INSTANCE (cust), value, 1, OWNER_EXPORT_PDF_DIRNAME);
175  break;
176  case PROP_LAST_POSTED:
177  qof_instance_set_kvp (QOF_INSTANCE (cust), value, 1, LAST_POSTED_TO_ACCT);
178  break;
179  case PROP_PAYMENT_LAST_ACCT:
180  qof_instance_set_kvp (QOF_INSTANCE (cust), value, 2, GNC_PAYMENT, GNC_LAST_ACCOUNT);
181  break;
182  default:
183  G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
184  break;
185  }
186 }
187 
189 static gchar*
190 impl_get_display_name(const QofInstance* inst)
191 {
192  GncCustomer* cust;
193 
194  g_return_val_if_fail(inst != NULL, FALSE);
195  g_return_val_if_fail(GNC_IS_CUSTOMER(inst), FALSE);
196 
197  cust = GNC_CUSTOMER(inst);
198  /* XXX internationalization of "Customer" */
199  return g_strdup_printf("Customer %s", cust->name);
200 }
201 
203 static gboolean
204 impl_refers_to_object(const QofInstance* inst, const QofInstance* ref)
205 {
206  GncCustomer* cust;
207 
208  g_return_val_if_fail(inst != NULL, FALSE);
209  g_return_val_if_fail(GNC_IS_CUSTOMER(inst), FALSE);
210 
211  cust = GNC_CUSTOMER(inst);
212 
213  if (GNC_IS_BILLTERM(ref))
214  {
215  return (cust->terms == GNC_BILLTERM(ref));
216  }
217  else if (GNC_IS_TAXTABLE(ref))
218  {
219  return (cust->taxtable == GNC_TAXTABLE(ref));
220  }
221 
222  return FALSE;
223 }
224 
231 static GList*
232 impl_get_typed_referring_object_list(const QofInstance* inst, const QofInstance* ref)
233 {
234  if (!GNC_IS_BILLTERM(ref) && !GNC_IS_TAXTABLE(ref))
235  {
236  return NULL;
237  }
238 
240 }
241 
242 static void
243 gnc_customer_class_init (GncCustomerClass *klass)
244 {
245  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
246  QofInstanceClass* qof_class = QOF_INSTANCE_CLASS(klass);
247 
248  gobject_class->dispose = gnc_customer_dispose;
249  gobject_class->finalize = gnc_customer_finalize;
250  gobject_class->set_property = gnc_customer_set_property;
251  gobject_class->get_property = gnc_customer_get_property;
252 
253  qof_class->get_display_name = impl_get_display_name;
254  qof_class->refers_to_object = impl_refers_to_object;
255  qof_class->get_typed_referring_object_list = impl_get_typed_referring_object_list;
256 
257  g_object_class_install_property
258  (gobject_class,
259  PROP_NAME,
260  g_param_spec_string ("name",
261  "Customer Name",
262  "The customer is an arbitrary string "
263  "assigned by the user which provides the "
264  "customer name.",
265  NULL,
266  G_PARAM_READWRITE));
267 
268  g_object_class_install_property
269  (gobject_class,
270  PROP_PDF_DIRNAME,
271  g_param_spec_string ("export-pdf-dir",
272  "Export PDF Directory Name",
273  "A subdirectory for exporting PDF reports which is "
274  "appended to the target directory when writing them "
275  "out. It is retrieved from preferences and stored on "
276  "each 'Owner' object which prints items after "
277  "printing.",
278  NULL,
279  G_PARAM_READWRITE));
280 
281  g_object_class_install_property(
282  gobject_class,
283  PROP_LAST_POSTED,
284  g_param_spec_boxed("invoice-last-posted-account",
285  "Invoice Last Posted Account",
286  "The last account to which an invoice belonging to "
287  "this owner was posted.",
288  GNC_TYPE_GUID,
289  G_PARAM_READWRITE));
290 
291  g_object_class_install_property(
292  gobject_class,
293  PROP_PAYMENT_LAST_ACCT,
294  g_param_spec_boxed("payment-last-account",
295  "Payment Last Account",
296  "The last account to which an payment belonging to "
297  "this owner was posted.",
298  GNC_TYPE_GUID,
299  G_PARAM_READWRITE));
300 }
301 
302 /* Create/Destroy Functions */
303 GncCustomer *gncCustomerCreate (QofBook *book)
304 {
305  GncCustomer *cust;
306 
307  if (!book) return NULL;
308 
309  cust = g_object_new (GNC_TYPE_CUSTOMER, NULL);
310  qof_instance_init_data (&cust->inst, _GNC_MOD_NAME, book);
311 
312  cust->id = CACHE_INSERT ("");
313  cust->name = CACHE_INSERT ("");
314  cust->notes = CACHE_INSERT ("");
315  cust->addr = gncAddressCreate (book, &cust->inst);
316  cust->taxincluded = GNC_TAXINCLUDED_USEGLOBAL;
317  cust->active = TRUE;
318  cust->jobs = NULL;
319  cust->balance = NULL;
320 
321  cust->discount = gnc_numeric_zero();
322  cust->credit = gnc_numeric_zero();
323  cust->shipaddr = gncAddressCreate (book, &cust->inst);
324 
325  if (cust_qof_event_handler_id == 0)
326  cust_qof_event_handler_id = qof_event_register_handler (cust_handle_qof_events, NULL);
327 
328  qof_event_gen (&cust->inst, QOF_EVENT_CREATE, NULL);
329 
330  return cust;
331 }
332 
333 void gncCustomerDestroy (GncCustomer *cust)
334 {
335  if (!cust) return;
336  qof_instance_set_destroying(cust, TRUE);
337  qof_instance_set_dirty (&cust->inst);
338  gncCustomerCommitEdit (cust);
339 }
340 
341 static void gncCustomerFree (GncCustomer *cust)
342 {
343  if (!cust) return;
344 
345  qof_event_gen (&cust->inst, QOF_EVENT_DESTROY, NULL);
346 
347  CACHE_REMOVE (cust->id);
348  CACHE_REMOVE (cust->name);
349  CACHE_REMOVE (cust->notes);
350  gncAddressBeginEdit (cust->addr);
351  gncAddressDestroy (cust->addr);
352  gncAddressBeginEdit (cust->shipaddr);
353  gncAddressDestroy (cust->shipaddr);
354 
355  gncJobFreeList (cust->jobs);
356  g_list_free (cust->jobs);
357  g_free (cust->balance);
358 
359  if (!qof_book_shutting_down (qof_instance_get_book (QOF_INSTANCE(cust))))
360  {
361  if (cust->terms)
362  gncBillTermDecRef (cust->terms);
363  if (cust->taxtable)
364  gncTaxTableDecRef (cust->taxtable);
365  }
366 
367  /* qof_instance_release (&cust->inst); */
368  g_object_unref (cust);
369 }
370 
371 /* ============================================================== */
372 /* Set Functions */
373 
374 #define SET_STR(obj, member, str) { \
375  if (!g_strcmp0 (member, str)) return; \
376  gncCustomerBeginEdit (obj); \
377  CACHE_REPLACE(member, str); \
378  }
379 
380 void gncCustomerSetID (GncCustomer *cust, const char *id)
381 {
382  if (!cust) return;
383  if (!id) return;
384  SET_STR(cust, cust->id, id);
385  mark_customer (cust);
386  gncCustomerCommitEdit (cust);
387 }
388 
389 void gncCustomerSetName (GncCustomer *cust, const char *name)
390 {
391  if (!cust) return;
392  if (!name) return;
393  SET_STR(cust, cust->name, name);
394  mark_customer (cust);
395  gncCustomerCommitEdit (cust);
396 }
397 
398 void gncCustomerSetNotes (GncCustomer *cust, const char *notes)
399 {
400  if (!cust) return;
401  if (!notes) return;
402  SET_STR(cust, cust->notes, notes);
403  mark_customer (cust);
404  gncCustomerCommitEdit (cust);
405 }
406 
407 void gncCustomerSetTerms (GncCustomer *cust, GncBillTerm *terms)
408 {
409  if (!cust) return;
410  if (cust->terms == terms) return;
411 
412  gncCustomerBeginEdit (cust);
413  if (cust->terms)
414  gncBillTermDecRef (cust->terms);
415  cust->terms = terms;
416  if (cust->terms)
417  gncBillTermIncRef (cust->terms);
418  mark_customer (cust);
419  gncCustomerCommitEdit (cust);
420 }
421 
422 void gncCustomerSetTaxIncluded (GncCustomer *cust, GncTaxIncluded taxincl)
423 {
424  if (!cust) return;
425  if (taxincl == cust->taxincluded) return;
426  gncCustomerBeginEdit (cust);
427  cust->taxincluded = taxincl;
428  mark_customer (cust);
429  gncCustomerCommitEdit (cust);
430 }
431 
432 void gncCustomerSetActive (GncCustomer *cust, gboolean active)
433 {
434  if (!cust) return;
435  if (active == cust->active) return;
436  gncCustomerBeginEdit (cust);
437  cust->active = active;
438  mark_customer (cust);
439  gncCustomerCommitEdit (cust);
440 }
441 
442 void gncCustomerSetDiscount (GncCustomer *cust, gnc_numeric discount)
443 {
444  if (!cust) return;
445  if (gnc_numeric_equal (discount, cust->discount)) return;
446  gncCustomerBeginEdit (cust);
447  cust->discount = discount;
448  mark_customer (cust);
449  gncCustomerCommitEdit (cust);
450 }
451 
452 void gncCustomerSetCredit (GncCustomer *cust, gnc_numeric credit)
453 {
454  if (!cust) return;
455  if (gnc_numeric_equal (credit, cust->credit)) return;
456  gncCustomerBeginEdit (cust);
457  cust->credit = credit;
458  mark_customer (cust);
459  gncCustomerCommitEdit (cust);
460 }
461 
462 void gncCustomerSetCurrency (GncCustomer *cust, gnc_commodity *currency)
463 {
464  if (!cust || !currency) return;
465  if (cust->currency && gnc_commodity_equal (cust->currency, currency)) return;
466  gncCustomerBeginEdit (cust);
467  cust->currency = currency;
468  mark_customer (cust);
469  gncCustomerCommitEdit (cust);
470 }
471 
472 void gncCustomerSetTaxTableOverride (GncCustomer *customer, gboolean override)
473 {
474  if (!customer) return;
475  if (customer->taxtable_override == override) return;
476  gncCustomerBeginEdit (customer);
477  customer->taxtable_override = override;
478  mark_customer (customer);
479  gncCustomerCommitEdit (customer);
480 }
481 
482 void gncCustomerSetTaxTable (GncCustomer *customer, GncTaxTable *table)
483 {
484  if (!customer) return;
485  if (customer->taxtable == table) return;
486 
487  gncCustomerBeginEdit (customer);
488  if (customer->taxtable)
489  gncTaxTableDecRef (customer->taxtable);
490  if (table)
491  gncTaxTableIncRef (table);
492  customer->taxtable = table;
493  mark_customer (customer);
494  gncCustomerCommitEdit (customer);
495 }
496 
497 /* Note that JobList changes do not affect the "dirtiness" of the customer */
498 void gncCustomerAddJob (GncCustomer *cust, GncJob *job)
499 {
500  if (!cust) return;
501  if (!job) return;
502 
503  if (g_list_index(cust->jobs, job) == -1)
504  cust->jobs = g_list_insert_sorted (cust->jobs, job,
505  (GCompareFunc)gncJobCompare);
506 
507  qof_event_gen (&cust->inst, QOF_EVENT_MODIFY, NULL);
508 }
509 
510 void gncCustomerRemoveJob (GncCustomer *cust, GncJob *job)
511 {
512  GList *node;
513 
514  if (!cust) return;
515  if (!job) return;
516 
517  node = g_list_find (cust->jobs, job);
518  if (!node)
519  {
520  /* PERR ("split not in account"); */
521  }
522  else
523  {
524  cust->jobs = g_list_remove_link (cust->jobs, node);
525  g_list_free_1 (node);
526  }
527  qof_event_gen (&cust->inst, QOF_EVENT_MODIFY, NULL);
528 }
529 
530 void gncCustomerBeginEdit (GncCustomer *cust)
531 {
532  qof_begin_edit (&cust->inst);
533 }
534 
535 static void gncCustomerOnError (QofInstance *inst, QofBackendError errcode)
536 {
537  PERR("Customer QofBackend Failure: %d", errcode);
538  gnc_engine_signal_commit_error( errcode );
539 }
540 
541 static void gncCustomerOnDone (QofInstance *inst)
542 {
543  GncCustomer *cust = (GncCustomer *) inst;
544  gncAddressClearDirty (cust->addr);
545  gncAddressClearDirty (cust->shipaddr);
546 }
547 
548 static void cust_free (QofInstance *inst)
549 {
550  GncCustomer *cust = (GncCustomer *) inst;
551  gncCustomerFree (cust);
552 }
553 
554 void gncCustomerCommitEdit (GncCustomer *cust)
555 {
556  if (!qof_commit_edit (QOF_INSTANCE(cust))) return;
557  qof_commit_edit_part2 (&cust->inst, gncCustomerOnError,
558  gncCustomerOnDone, cust_free);
559 }
560 
561 /* ============================================================== */
562 /* Get Functions */
563 
564 const char * gncCustomerGetID (const GncCustomer *cust)
565 {
566  if (!cust) return NULL;
567  return cust->id;
568 }
569 
570 const char * gncCustomerGetName (const GncCustomer *cust)
571 {
572  if (!cust) return NULL;
573  return cust->name;
574 }
575 
576 GncAddress * gncCustomerGetAddr (const GncCustomer *cust)
577 {
578  if (!cust) return NULL;
579  return cust->addr;
580 }
581 
582 static void
583 qofCustomerSetAddr (GncCustomer *cust, QofInstance *addr_ent)
584 {
585  GncAddress *addr;
586 
587  if (!cust || !addr_ent)
588  {
589  return;
590  }
591  addr = (GncAddress*)addr_ent;
592  if (addr == cust->addr)
593  {
594  return;
595  }
596  if (cust->addr != NULL)
597  {
598  gncAddressBeginEdit(cust->addr);
599  gncAddressDestroy(cust->addr);
600  }
601  gncCustomerBeginEdit(cust);
602  cust->addr = addr;
603  gncCustomerCommitEdit(cust);
604 }
605 
606 static void
607 qofCustomerSetShipAddr (GncCustomer *cust, QofInstance *ship_addr_ent)
608 {
609  GncAddress *ship_addr;
610 
611  if (!cust || !ship_addr_ent)
612  {
613  return;
614  }
615  ship_addr = (GncAddress*)ship_addr_ent;
616  if (ship_addr == cust->shipaddr)
617  {
618  return;
619  }
620  if (cust->shipaddr != NULL)
621  {
622  gncAddressBeginEdit(cust->shipaddr);
623  gncAddressDestroy(cust->shipaddr);
624  }
625  gncCustomerBeginEdit(cust);
626  cust->shipaddr = ship_addr;
627  gncCustomerCommitEdit(cust);
628 }
629 
630 GncAddress * gncCustomerGetShipAddr (const GncCustomer *cust)
631 {
632  if (!cust) return NULL;
633  return cust->shipaddr;
634 }
635 
636 const char * gncCustomerGetNotes (const GncCustomer *cust)
637 {
638  if (!cust) return NULL;
639  return cust->notes;
640 }
641 
642 GncBillTerm * gncCustomerGetTerms (const GncCustomer *cust)
643 {
644  if (!cust) return NULL;
645  return cust->terms;
646 }
647 
648 GncTaxIncluded gncCustomerGetTaxIncluded (const GncCustomer *cust)
649 {
650  if (!cust) return GNC_TAXINCLUDED_USEGLOBAL;
651  return cust->taxincluded;
652 }
653 
654 gnc_commodity * gncCustomerGetCurrency (const GncCustomer *cust)
655 {
656  if (!cust) return NULL;
657  return cust->currency;
658 }
659 
660 gboolean gncCustomerGetActive (const GncCustomer *cust)
661 {
662  if (!cust) return FALSE;
663  return cust->active;
664 }
665 
666 gnc_numeric gncCustomerGetDiscount (const GncCustomer *cust)
667 {
668  if (!cust) return gnc_numeric_zero();
669  return cust->discount;
670 }
671 
672 gnc_numeric gncCustomerGetCredit (const GncCustomer *cust)
673 {
674  if (!cust) return gnc_numeric_zero();
675  return cust->credit;
676 }
677 
678 gboolean gncCustomerGetTaxTableOverride (const GncCustomer *customer)
679 {
680  if (!customer) return FALSE;
681  return customer->taxtable_override;
682 }
683 
684 GncTaxTable* gncCustomerGetTaxTable (const GncCustomer *customer)
685 {
686  if (!customer) return NULL;
687  return customer->taxtable;
688 }
689 
690 GList * gncCustomerGetJoblist (const GncCustomer *cust, gboolean show_all)
691 {
692  if (!cust) return NULL;
693 
694  if (show_all)
695  {
696  return (g_list_copy (cust->jobs));
697  }
698  else
699  {
700  GList *list = NULL, *iterator;
701  for (iterator = cust->jobs; iterator; iterator = iterator->next)
702  {
703  GncJob *j = iterator->data;
704  if (gncJobGetActive (j))
705  list = g_list_prepend (list, j);
706  }
707  return g_list_reverse (list);
708  }
709 }
710 
711 gboolean gncCustomerIsDirty (GncCustomer *cust)
712 {
713  if (!cust) return FALSE;
714  return (qof_instance_is_dirty(&cust->inst) ||
715  gncAddressIsDirty (cust->addr) ||
716  gncAddressIsDirty (cust->shipaddr));
717 }
718 
719 /* Other functions */
720 
721 int gncCustomerCompare (const GncCustomer *a, const GncCustomer *b)
722 {
723  if (!a && !b) return 0;
724  if (!a && b) return 1;
725  if (a && !b) return -1;
726 
727  return(strcmp(a->name, b->name));
728 }
729 
730 gboolean
732 {
733  if (a == NULL && b == NULL) return TRUE;
734  if (a == NULL || b == NULL) return FALSE;
735 
736  g_return_val_if_fail(GNC_IS_CUSTOMER(a), FALSE);
737  g_return_val_if_fail(GNC_IS_CUSTOMER(b), FALSE);
738 
739  if (g_strcmp0(a->id, b->id) != 0)
740  {
741  PWARN("IDs differ: %s vs %s", a->id, b->id);
742  return FALSE;
743  }
744 
745  if (g_strcmp0(a->name, b->name) != 0)
746  {
747  PWARN("Names differ: %s vs %s", a->name, b->name);
748  return FALSE;
749  }
750 
751  if (g_strcmp0(a->notes, b->notes) != 0)
752  {
753  PWARN("Notes differ: %s vs %s", a->notes, b->notes);
754  return FALSE;
755  }
756 
757  if (!gncBillTermEqual(a->terms, b->terms))
758  {
759  PWARN("Bill terms differ");
760  return FALSE;
761  }
762 
763  if (!gnc_commodity_equal(a->currency, b->currency))
764  {
765  PWARN("currencies differ");
766  return FALSE;
767  }
768 
769  if (!gncTaxTableEqual(a->taxtable, b->taxtable))
770  {
771  PWARN("tax tables differ");
772  return FALSE;
773  }
774 
775  if (a->taxtable_override != b->taxtable_override)
776  {
777  PWARN("Tax table override flags differ");
778  return FALSE;
779  }
780 
781  if (a->taxincluded != b->taxincluded)
782  {
783  PWARN("Tax included flags differ");
784  return FALSE;
785  }
786 
787  if (a->active != b->active)
788  {
789  PWARN("Active flags differ");
790  return FALSE;
791  }
792 
793  if (!gncAddressEqual(a->addr, b->addr))
794  {
795  PWARN("addresses differ");
796  return FALSE;
797  }
798  if (!gncAddressEqual(a->shipaddr, b->shipaddr))
799  {
800  PWARN("addresses differ");
801  return FALSE;
802  }
803 
804  if (!gnc_numeric_equal(a->credit, b->credit))
805  {
806  PWARN("Credit amounts differ");
807  return FALSE;
808  }
809 
810  if (!gnc_numeric_equal(a->discount, b->discount))
811  {
812  PWARN("Discount amounts differ");
813  return FALSE;
814  }
815 
816  /* FIXME: Need to check jobs list
817  GList * jobs;
818  */
819 
820  return TRUE;
821 }
822 
835 static void
836 cust_handle_qof_events (QofInstance *entity, QofEventId event_type,
837  gpointer user_data, gpointer event_data)
838 {
839  /* Handle address change events */
840  if ((GNC_IS_ADDRESS (entity) &&
841  (event_type & QOF_EVENT_MODIFY) != 0))
842  {
843  if (GNC_IS_CUSTOMER (event_data))
844  {
845  GncCustomer* cust = GNC_CUSTOMER (event_data);
846  gncCustomerBeginEdit (cust);
847  mark_customer (cust);
848  gncCustomerCommitEdit (cust);
849  }
850  return;
851  }
852 
853  /* Handle lot change events */
854  if (GNC_IS_LOT (entity))
855  {
856  GNCLot *lot = GNC_LOT (entity);
857  GncOwner lot_owner;
858  const GncOwner *end_owner = NULL;
859  GncInvoice *invoice = gncInvoiceGetInvoiceFromLot (lot);
860 
861  /* Determine the owner associated with the lot */
862  if (invoice)
863  /* Invoice lots */
864  end_owner = gncOwnerGetEndOwner (gncInvoiceGetOwner (invoice));
865  else if (gncOwnerGetOwnerFromLot (lot, &lot_owner))
866  /* Pre-payment lots */
867  end_owner = gncOwnerGetEndOwner (&lot_owner);
868 
869  if (gncOwnerGetType (end_owner) == GNC_OWNER_CUSTOMER)
870  {
871  /* Clear the cached balance */
872  GncCustomer* cust = gncOwnerGetCustomer (end_owner);
873  g_free (cust->balance);
874  cust->balance = NULL;
875  }
876  return;
877  }
878 }
879 
880 /* ============================================================== */
881 /* Package-Private functions */
882 static const char * _gncCustomerPrintable (gpointer item)
883 {
884 // GncCustomer *c = item;
885  if (!item) return "failed";
886  return gncCustomerGetName((GncCustomer*)item);
887 }
888 
889 static void
890 destroy_customer_on_book_close(QofInstance *ent, gpointer data)
891 {
892  GncCustomer* c = GNC_CUSTOMER(ent);
893 
894  gncCustomerBeginEdit(c);
895  gncCustomerDestroy(c);
896 }
897 
902 static void
903 gnc_customer_book_end(QofBook* book)
904 {
905  QofCollection *col;
906 
907  col = qof_book_get_collection(book, GNC_ID_CUSTOMER);
908  qof_collection_foreach(col, destroy_customer_on_book_close, NULL);
909 }
910 
911 static QofObject gncCustomerDesc =
912 {
913  DI(.interface_version = ) QOF_OBJECT_VERSION,
914  DI(.e_type = ) _GNC_MOD_NAME,
915  DI(.type_label = ) "Customer",
916  DI(.create = ) (gpointer)gncCustomerCreate,
917  DI(.book_begin = ) NULL,
918  DI(.book_end = ) gnc_customer_book_end,
919  DI(.is_dirty = ) qof_collection_is_dirty,
920  DI(.mark_clean = ) qof_collection_mark_clean,
921  DI(.foreach = ) qof_collection_foreach,
922  DI(.printable = ) (const char * (*)(gpointer))gncCustomerGetName,
923  DI(.version_cmp = ) (int (*)(gpointer, gpointer)) qof_instance_version_cmp,
924 };
925 
926 gboolean gncCustomerRegister (void)
927 {
928  static QofParam params[] =
929  {
930  { CUSTOMER_ID, QOF_TYPE_STRING, (QofAccessFunc)gncCustomerGetID, (QofSetterFunc)gncCustomerSetID },
931  { CUSTOMER_NAME, QOF_TYPE_STRING, (QofAccessFunc)gncCustomerGetName, (QofSetterFunc)gncCustomerSetName },
932  { CUSTOMER_NOTES, QOF_TYPE_STRING, (QofAccessFunc)gncCustomerGetNotes, (QofSetterFunc)gncCustomerSetNotes },
933  {
934  CUSTOMER_DISCOUNT, QOF_TYPE_NUMERIC, (QofAccessFunc)gncCustomerGetDiscount,
935  (QofSetterFunc)gncCustomerSetDiscount
936  },
937  {
938  CUSTOMER_CREDIT, QOF_TYPE_NUMERIC, (QofAccessFunc)gncCustomerGetCredit,
939  (QofSetterFunc)gncCustomerSetCredit
940  },
941  { CUSTOMER_ADDR, GNC_ID_ADDRESS, (QofAccessFunc)gncCustomerGetAddr, (QofSetterFunc)qofCustomerSetAddr },
942  { CUSTOMER_SHIPADDR, GNC_ID_ADDRESS, (QofAccessFunc)gncCustomerGetShipAddr, (QofSetterFunc)qofCustomerSetShipAddr },
943  {
944  CUSTOMER_TT_OVER, QOF_TYPE_BOOLEAN, (QofAccessFunc)gncCustomerGetTaxTableOverride,
945  (QofSetterFunc)gncCustomerSetTaxTableOverride
946  },
947  { CUSTOMER_TERMS, GNC_ID_BILLTERM, (QofAccessFunc)gncCustomerGetTerms, (QofSetterFunc)gncCustomerSetTerms },
948  { QOF_PARAM_ACTIVE, QOF_TYPE_BOOLEAN, (QofAccessFunc)gncCustomerGetActive, (QofSetterFunc)gncCustomerSetActive },
949  { QOF_PARAM_BOOK, QOF_ID_BOOK, (QofAccessFunc)qof_instance_get_book, NULL },
950  { QOF_PARAM_GUID, QOF_TYPE_GUID, (QofAccessFunc)qof_instance_get_guid, NULL },
951  { NULL },
952  };
953 
954  qof_class_register (_GNC_MOD_NAME, (QofSortFunc)gncCustomerCompare, params);
955  /* temp */
956  _gncCustomerPrintable(NULL);
957  return qof_object_register (&gncCustomerDesc);
958 }
959 
960 gchar *gncCustomerNextID (QofBook *book)
961 {
962  return qof_book_increment_and_format_counter (book, _GNC_MOD_NAME);
963 }
964 
965 const gnc_numeric*
966 gncCustomerGetCachedBalance (GncCustomer *cust)
967 {
968  return cust->balance;
969 }
970 
971 void gncCustomerSetCachedBalance (GncCustomer *cust, const gnc_numeric *new_bal)
972 {
973  if (!new_bal)
974  {
975  if (cust->balance)
976  {
977  g_free (cust->balance);
978  cust->balance = NULL;
979  }
980  return;
981  }
982 
983  if (!cust->balance)
984  cust->balance = g_new0 (gnc_numeric, 1);
985 
986  *cust->balance = *new_bal;
987 }
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:166
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:255
GncTaxIncluded
How to interpret the TaxIncluded.
Definition: gncTaxTable.h:85
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:625
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:223
#define QOF_OBJECT_VERSION
Defines the version of the core object object registration interface.
Definition: qofobject.h:63
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:108
use the global setting
Definition: gncTaxTable.h:89
void(* QofSetterFunc)(gpointer, gpointer)
The QofSetterFunc defines an function pointer for parameter setters.
Definition: qofclass.h:185
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
– 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:636
gpointer(* QofAccessFunc)(gpointer object, const QofParam *param)
The QofAccessFunc defines an arbitrary function pointer for access functions.
Definition: qofclass.h:178
void qof_collection_mark_clean(QofCollection *)
reset value of dirty flag
Definition: qofid.cpp:261
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:200
const GncOwner * gncOwnerGetEndOwner(const GncOwner *owner)
Get the "parent" Owner or GncGUID thereof.
Definition: gncOwner.c:572
GncInvoice * gncInvoiceGetInvoiceFromLot(GNCLot *lot)
Given a LOT, find and return the Invoice attached to the lot.
Definition: gncInvoice.c:1289
Business Invoice Interface.
gboolean gncCustomerEqual(const GncCustomer *a, const GncCustomer *b)
Test support function, used in gets-dbi-business-stuff.c.
Definition: gncCustomer.c:731
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:369
QofCollection * qof_book_get_collection(const QofBook *book, QofIdType entity_type)
Return The table of entities of the given type.
Definition: qofbook.cpp:521
gboolean qof_object_register(const QofObject *object)
Register new types of object objects.
Definition: qofobject.cpp:299
gboolean qof_book_shutting_down(const QofBook *book)
Is the book shutting down?
Definition: qofbook.cpp:447
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 libgnucash/engine/TaxTableBillTermImmutability.txt for an explanation of the following Code that handles refcount, parent, child, invisible and children is identical to that in ::GncBillTerm