Line data Source code
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 :
51 : struct _gncCustomer
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 :
75 : struct _gncCustomerClass
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 96 : void mark_customer (GncCustomer *customer)
89 : {
90 96 : qof_instance_set_dirty(&customer->inst);
91 96 : qof_event_gen (&customer->inst, QOF_EVENT_MODIFY, NULL);
92 96 : }
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 141 : G_DEFINE_TYPE(GncCustomer, gnc_customer, QOF_TYPE_INSTANCE)
107 :
108 : static void
109 37 : gnc_customer_init(GncCustomer* cust)
110 : {
111 37 : }
112 :
113 : static void
114 22 : gnc_customer_dispose(GObject *custp)
115 : {
116 22 : G_OBJECT_CLASS(gnc_customer_parent_class)->dispose(custp);
117 22 : }
118 :
119 : static void
120 22 : gnc_customer_finalize(GObject* custp)
121 : {
122 22 : G_OBJECT_CLASS(gnc_customer_parent_class)->finalize(custp);
123 22 : }
124 :
125 : static void
126 4 : gnc_customer_get_property (GObject *object,
127 : guint prop_id,
128 : GValue *value,
129 : GParamSpec *pspec)
130 : {
131 : GncCustomer *cust;
132 4 : g_return_if_fail(GNC_IS_CUSTOMER(object));
133 :
134 4 : cust = GNC_CUSTOMER(object);
135 4 : switch (prop_id)
136 : {
137 1 : case PROP_NAME:
138 1 : g_value_set_string(value, cust->name);
139 1 : break;
140 1 : case PROP_PDF_DIRNAME:
141 1 : qof_instance_get_kvp (QOF_INSTANCE (cust), value, 1, OWNER_EXPORT_PDF_DIRNAME);
142 1 : break;
143 1 : case PROP_LAST_POSTED:
144 1 : qof_instance_get_kvp (QOF_INSTANCE (cust), value, 1, LAST_POSTED_TO_ACCT);
145 1 : break;
146 1 : case PROP_PAYMENT_LAST_ACCT:
147 1 : qof_instance_get_kvp (QOF_INSTANCE (cust), value, 2, GNC_PAYMENT, GNC_LAST_ACCOUNT);
148 1 : break;
149 0 : default:
150 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
151 0 : break;
152 : }
153 : }
154 :
155 : static void
156 4 : gnc_customer_set_property (GObject *object,
157 : guint prop_id,
158 : const GValue *value,
159 : GParamSpec *pspec)
160 : {
161 : GncCustomer *cust;
162 :
163 4 : g_return_if_fail(GNC_IS_CUSTOMER(object));
164 :
165 4 : cust = GNC_CUSTOMER(object);
166 4 : g_assert (qof_instance_get_editlevel(cust));
167 :
168 4 : switch (prop_id)
169 : {
170 1 : case PROP_NAME:
171 1 : gncCustomerSetName(cust, g_value_get_string(value));
172 1 : break;
173 1 : case PROP_PDF_DIRNAME:
174 1 : qof_instance_set_kvp (QOF_INSTANCE (cust), value, 1, OWNER_EXPORT_PDF_DIRNAME);
175 1 : break;
176 1 : case PROP_LAST_POSTED:
177 1 : qof_instance_set_kvp (QOF_INSTANCE (cust), value, 1, LAST_POSTED_TO_ACCT);
178 1 : break;
179 1 : case PROP_PAYMENT_LAST_ACCT:
180 1 : qof_instance_set_kvp (QOF_INSTANCE (cust), value, 2, GNC_PAYMENT, GNC_LAST_ACCOUNT);
181 1 : break;
182 0 : default:
183 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
184 0 : break;
185 : }
186 : }
187 :
188 : /** Return display name for this object */
189 : static gchar*
190 0 : impl_get_display_name(const QofInstance* inst)
191 : {
192 : GncCustomer* cust;
193 :
194 0 : g_return_val_if_fail(inst != NULL, FALSE);
195 0 : g_return_val_if_fail(GNC_IS_CUSTOMER(inst), FALSE);
196 :
197 0 : cust = GNC_CUSTOMER(inst);
198 : /* XXX internationalization of "Customer" */
199 0 : return g_strdup_printf("Customer %s", cust->name);
200 : }
201 :
202 : /** Does this object refer to a specific object */
203 : static gboolean
204 0 : impl_refers_to_object(const QofInstance* inst, const QofInstance* ref)
205 : {
206 : GncCustomer* cust;
207 :
208 0 : g_return_val_if_fail(inst != NULL, FALSE);
209 0 : g_return_val_if_fail(GNC_IS_CUSTOMER(inst), FALSE);
210 :
211 0 : cust = GNC_CUSTOMER(inst);
212 :
213 0 : if (GNC_IS_BILLTERM(ref))
214 : {
215 0 : return (cust->terms == GNC_BILLTERM(ref));
216 : }
217 0 : else if (GNC_IS_TAXTABLE(ref))
218 : {
219 0 : return (cust->taxtable == GNC_TAXTABLE(ref));
220 : }
221 :
222 0 : return FALSE;
223 : }
224 :
225 : /** Returns a list of my type of object which refers to an object. For example, when called as
226 : qof_instance_get_typed_referring_object_list(taxtable, account);
227 : it will return the list of taxtables which refer to a specific account. The result should be the
228 : same regardless of which taxtable object is used. The list must be freed by the caller but the
229 : objects on the list must not.
230 : */
231 : static GList*
232 0 : impl_get_typed_referring_object_list(const QofInstance* inst, const QofInstance* ref)
233 : {
234 0 : if (!GNC_IS_BILLTERM(ref) && !GNC_IS_TAXTABLE(ref))
235 : {
236 0 : return NULL;
237 : }
238 :
239 0 : return qof_instance_get_referring_object_list_from_collection(qof_instance_get_collection(inst), ref);
240 : }
241 :
242 : static void
243 13 : gnc_customer_class_init (GncCustomerClass *klass)
244 : {
245 13 : GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
246 13 : QofInstanceClass* qof_class = QOF_INSTANCE_CLASS(klass);
247 :
248 13 : gobject_class->dispose = gnc_customer_dispose;
249 13 : gobject_class->finalize = gnc_customer_finalize;
250 13 : gobject_class->set_property = gnc_customer_set_property;
251 13 : gobject_class->get_property = gnc_customer_get_property;
252 :
253 13 : qof_class->get_display_name = impl_get_display_name;
254 13 : qof_class->refers_to_object = impl_refers_to_object;
255 13 : qof_class->get_typed_referring_object_list = impl_get_typed_referring_object_list;
256 :
257 13 : 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 13 : 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 13 : 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 13 : 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 13 : }
301 :
302 : /* Create/Destroy Functions */
303 38 : GncCustomer *gncCustomerCreate (QofBook *book)
304 : {
305 : GncCustomer *cust;
306 :
307 38 : if (!book) return NULL;
308 :
309 37 : cust = g_object_new (GNC_TYPE_CUSTOMER, NULL);
310 37 : qof_instance_init_data (&cust->inst, _GNC_MOD_NAME, book);
311 :
312 37 : cust->id = CACHE_INSERT ("");
313 37 : cust->name = CACHE_INSERT ("");
314 37 : cust->notes = CACHE_INSERT ("");
315 37 : cust->addr = gncAddressCreate (book, &cust->inst);
316 37 : cust->taxincluded = GNC_TAXINCLUDED_USEGLOBAL;
317 37 : cust->active = TRUE;
318 37 : cust->jobs = NULL;
319 37 : cust->balance = NULL;
320 :
321 37 : cust->discount = gnc_numeric_zero();
322 37 : cust->credit = gnc_numeric_zero();
323 37 : cust->shipaddr = gncAddressCreate (book, &cust->inst);
324 :
325 37 : if (cust_qof_event_handler_id == 0)
326 13 : cust_qof_event_handler_id = qof_event_register_handler (cust_handle_qof_events, NULL);
327 :
328 37 : qof_event_gen (&cust->inst, QOF_EVENT_CREATE, NULL);
329 :
330 37 : return cust;
331 : }
332 :
333 21 : void gncCustomerDestroy (GncCustomer *cust)
334 : {
335 21 : if (!cust) return;
336 21 : qof_instance_set_destroying(cust, TRUE);
337 21 : qof_instance_set_dirty (&cust->inst);
338 21 : gncCustomerCommitEdit (cust);
339 : }
340 :
341 21 : static void gncCustomerFree (GncCustomer *cust)
342 : {
343 21 : if (!cust) return;
344 :
345 21 : qof_event_gen (&cust->inst, QOF_EVENT_DESTROY, NULL);
346 :
347 21 : CACHE_REMOVE (cust->id);
348 21 : CACHE_REMOVE (cust->name);
349 21 : CACHE_REMOVE (cust->notes);
350 21 : gncAddressBeginEdit (cust->addr);
351 21 : gncAddressDestroy (cust->addr);
352 21 : gncAddressBeginEdit (cust->shipaddr);
353 21 : gncAddressDestroy (cust->shipaddr);
354 :
355 21 : gncJobFreeList (cust->jobs);
356 21 : g_list_free (cust->jobs);
357 21 : g_free (cust->balance);
358 :
359 21 : if (!qof_book_shutting_down (qof_instance_get_book (QOF_INSTANCE(cust))))
360 : {
361 9 : if (cust->terms)
362 0 : gncBillTermDecRef (cust->terms);
363 9 : if (cust->taxtable)
364 0 : gncTaxTableDecRef (cust->taxtable);
365 : }
366 :
367 : /* qof_instance_release (&cust->inst); */
368 21 : 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 19 : void gncCustomerSetID (GncCustomer *cust, const char *id)
381 : {
382 19 : if (!cust) return;
383 19 : if (!id) return;
384 19 : SET_STR(cust, cust->id, id);
385 19 : mark_customer (cust);
386 19 : gncCustomerCommitEdit (cust);
387 : }
388 :
389 13 : void gncCustomerSetName (GncCustomer *cust, const char *name)
390 : {
391 13 : if (!cust) return;
392 13 : if (!name) return;
393 13 : SET_STR(cust, cust->name, name);
394 13 : mark_customer (cust);
395 13 : gncCustomerCommitEdit (cust);
396 : }
397 :
398 12 : void gncCustomerSetNotes (GncCustomer *cust, const char *notes)
399 : {
400 12 : if (!cust) return;
401 12 : if (!notes) return;
402 12 : SET_STR(cust, cust->notes, notes);
403 12 : mark_customer (cust);
404 12 : gncCustomerCommitEdit (cust);
405 : }
406 :
407 0 : void gncCustomerSetTerms (GncCustomer *cust, GncBillTerm *terms)
408 : {
409 0 : if (!cust) return;
410 0 : if (cust->terms == terms) return;
411 :
412 0 : gncCustomerBeginEdit (cust);
413 0 : if (cust->terms)
414 0 : gncBillTermDecRef (cust->terms);
415 0 : cust->terms = terms;
416 0 : if (cust->terms)
417 0 : gncBillTermIncRef (cust->terms);
418 0 : mark_customer (cust);
419 0 : gncCustomerCommitEdit (cust);
420 : }
421 :
422 10 : void gncCustomerSetTaxIncluded (GncCustomer *cust, GncTaxIncluded taxincl)
423 : {
424 10 : if (!cust) return;
425 10 : if (taxincl == cust->taxincluded) return;
426 8 : gncCustomerBeginEdit (cust);
427 8 : cust->taxincluded = taxincl;
428 8 : mark_customer (cust);
429 8 : gncCustomerCommitEdit (cust);
430 : }
431 :
432 11 : void gncCustomerSetActive (GncCustomer *cust, gboolean active)
433 : {
434 11 : if (!cust) return;
435 11 : if (active == cust->active) return;
436 8 : gncCustomerBeginEdit (cust);
437 8 : cust->active = active;
438 8 : mark_customer (cust);
439 8 : gncCustomerCommitEdit (cust);
440 : }
441 :
442 3 : void gncCustomerSetDiscount (GncCustomer *cust, gnc_numeric discount)
443 : {
444 3 : if (!cust) return;
445 3 : if (gnc_numeric_equal (discount, cust->discount)) return;
446 1 : gncCustomerBeginEdit (cust);
447 1 : cust->discount = discount;
448 1 : mark_customer (cust);
449 1 : gncCustomerCommitEdit (cust);
450 : }
451 :
452 3 : void gncCustomerSetCredit (GncCustomer *cust, gnc_numeric credit)
453 : {
454 3 : if (!cust) return;
455 3 : if (gnc_numeric_equal (credit, cust->credit)) return;
456 1 : gncCustomerBeginEdit (cust);
457 1 : cust->credit = credit;
458 1 : mark_customer (cust);
459 1 : gncCustomerCommitEdit (cust);
460 : }
461 :
462 18 : void gncCustomerSetCurrency (GncCustomer *cust, gnc_commodity *currency)
463 : {
464 18 : if (!cust || !currency) return;
465 18 : if (cust->currency && gnc_commodity_equal (cust->currency, currency)) return;
466 18 : gncCustomerBeginEdit (cust);
467 18 : cust->currency = currency;
468 18 : mark_customer (cust);
469 18 : gncCustomerCommitEdit (cust);
470 : }
471 :
472 2 : void gncCustomerSetTaxTableOverride (GncCustomer *customer, gboolean override)
473 : {
474 2 : if (!customer) return;
475 2 : if (customer->taxtable_override == override) return;
476 0 : gncCustomerBeginEdit (customer);
477 0 : customer->taxtable_override = override;
478 0 : mark_customer (customer);
479 0 : gncCustomerCommitEdit (customer);
480 : }
481 :
482 0 : void gncCustomerSetTaxTable (GncCustomer *customer, GncTaxTable *table)
483 : {
484 0 : if (!customer) return;
485 0 : if (customer->taxtable == table) return;
486 :
487 0 : gncCustomerBeginEdit (customer);
488 0 : if (customer->taxtable)
489 0 : gncTaxTableDecRef (customer->taxtable);
490 0 : if (table)
491 0 : gncTaxTableIncRef (table);
492 0 : customer->taxtable = table;
493 0 : mark_customer (customer);
494 0 : gncCustomerCommitEdit (customer);
495 : }
496 :
497 : /* Note that JobList changes do not affect the "dirtiness" of the customer */
498 15 : void gncCustomerAddJob (GncCustomer *cust, GncJob *job)
499 : {
500 15 : if (!cust) return;
501 15 : if (!job) return;
502 :
503 15 : if (g_list_index(cust->jobs, job) == -1)
504 15 : cust->jobs = g_list_insert_sorted (cust->jobs, job,
505 : (GCompareFunc)gncJobCompare);
506 :
507 15 : qof_event_gen (&cust->inst, QOF_EVENT_MODIFY, NULL);
508 : }
509 :
510 2 : void gncCustomerRemoveJob (GncCustomer *cust, GncJob *job)
511 : {
512 : GList *node;
513 :
514 2 : if (!cust) return;
515 2 : if (!job) return;
516 :
517 2 : node = g_list_find (cust->jobs, job);
518 2 : if (!node)
519 : {
520 : /* PERR ("split not in account"); */
521 : }
522 : else
523 : {
524 2 : cust->jobs = g_list_remove_link (cust->jobs, node);
525 2 : g_list_free_1 (node);
526 : }
527 2 : qof_event_gen (&cust->inst, QOF_EVENT_MODIFY, NULL);
528 : }
529 :
530 134 : void gncCustomerBeginEdit (GncCustomer *cust)
531 : {
532 134 : qof_begin_edit (&cust->inst);
533 134 : }
534 :
535 0 : static void gncCustomerOnError (QofInstance *inst, QofBackendError errcode)
536 : {
537 0 : PERR("Customer QofBackend Failure: %d", errcode);
538 0 : gnc_engine_signal_commit_error( errcode );
539 0 : }
540 :
541 78 : static void gncCustomerOnDone (QofInstance *inst)
542 : {
543 78 : GncCustomer *cust = (GncCustomer *) inst;
544 78 : gncAddressClearDirty (cust->addr);
545 78 : gncAddressClearDirty (cust->shipaddr);
546 78 : }
547 :
548 21 : static void cust_free (QofInstance *inst)
549 : {
550 21 : GncCustomer *cust = (GncCustomer *) inst;
551 21 : gncCustomerFree (cust);
552 21 : }
553 :
554 134 : void gncCustomerCommitEdit (GncCustomer *cust)
555 : {
556 134 : if (!qof_commit_edit (QOF_INSTANCE(cust))) return;
557 99 : qof_commit_edit_part2 (&cust->inst, gncCustomerOnError,
558 : gncCustomerOnDone, cust_free);
559 : }
560 :
561 : /* ============================================================== */
562 : /* Get Functions */
563 :
564 4 : const char * gncCustomerGetID (const GncCustomer *cust)
565 : {
566 4 : if (!cust) return NULL;
567 4 : return cust->id;
568 : }
569 :
570 122 : const char * gncCustomerGetName (const GncCustomer *cust)
571 : {
572 122 : if (!cust) return NULL;
573 122 : return cust->name;
574 : }
575 :
576 25 : GncAddress * gncCustomerGetAddr (const GncCustomer *cust)
577 : {
578 25 : if (!cust) return NULL;
579 25 : return cust->addr;
580 : }
581 :
582 : static void
583 1 : qofCustomerSetAddr (GncCustomer *cust, QofInstance *addr_ent)
584 : {
585 : GncAddress *addr;
586 :
587 1 : if (!cust || !addr_ent)
588 : {
589 0 : return;
590 : }
591 1 : addr = (GncAddress*)addr_ent;
592 1 : if (addr == cust->addr)
593 : {
594 0 : return;
595 : }
596 1 : if (cust->addr != NULL)
597 : {
598 1 : gncAddressBeginEdit(cust->addr);
599 1 : gncAddressDestroy(cust->addr);
600 : }
601 1 : gncCustomerBeginEdit(cust);
602 1 : cust->addr = addr;
603 1 : gncCustomerCommitEdit(cust);
604 : }
605 :
606 : static void
607 1 : qofCustomerSetShipAddr (GncCustomer *cust, QofInstance *ship_addr_ent)
608 : {
609 : GncAddress *ship_addr;
610 :
611 1 : if (!cust || !ship_addr_ent)
612 : {
613 0 : return;
614 : }
615 1 : ship_addr = (GncAddress*)ship_addr_ent;
616 1 : if (ship_addr == cust->shipaddr)
617 : {
618 0 : return;
619 : }
620 1 : if (cust->shipaddr != NULL)
621 : {
622 1 : gncAddressBeginEdit(cust->shipaddr);
623 1 : gncAddressDestroy(cust->shipaddr);
624 : }
625 1 : gncCustomerBeginEdit(cust);
626 1 : cust->shipaddr = ship_addr;
627 1 : gncCustomerCommitEdit(cust);
628 : }
629 :
630 4 : GncAddress * gncCustomerGetShipAddr (const GncCustomer *cust)
631 : {
632 4 : if (!cust) return NULL;
633 4 : return cust->shipaddr;
634 : }
635 :
636 2 : const char * gncCustomerGetNotes (const GncCustomer *cust)
637 : {
638 2 : if (!cust) return NULL;
639 2 : return cust->notes;
640 : }
641 :
642 2 : GncBillTerm * gncCustomerGetTerms (const GncCustomer *cust)
643 : {
644 2 : if (!cust) return NULL;
645 2 : return cust->terms;
646 : }
647 :
648 1 : GncTaxIncluded gncCustomerGetTaxIncluded (const GncCustomer *cust)
649 : {
650 1 : if (!cust) return GNC_TAXINCLUDED_USEGLOBAL;
651 1 : return cust->taxincluded;
652 : }
653 :
654 3 : gnc_commodity * gncCustomerGetCurrency (const GncCustomer *cust)
655 : {
656 3 : if (!cust) return NULL;
657 3 : return cust->currency;
658 : }
659 :
660 15 : gboolean gncCustomerGetActive (const GncCustomer *cust)
661 : {
662 15 : if (!cust) return FALSE;
663 15 : return cust->active;
664 : }
665 :
666 2 : gnc_numeric gncCustomerGetDiscount (const GncCustomer *cust)
667 : {
668 2 : if (!cust) return gnc_numeric_zero();
669 2 : return cust->discount;
670 : }
671 :
672 2 : gnc_numeric gncCustomerGetCredit (const GncCustomer *cust)
673 : {
674 2 : if (!cust) return gnc_numeric_zero();
675 2 : return cust->credit;
676 : }
677 :
678 1 : gboolean gncCustomerGetTaxTableOverride (const GncCustomer *customer)
679 : {
680 1 : if (!customer) return FALSE;
681 1 : return customer->taxtable_override;
682 : }
683 :
684 2 : GncTaxTable* gncCustomerGetTaxTable (const GncCustomer *customer)
685 : {
686 2 : if (!customer) return NULL;
687 2 : return customer->taxtable;
688 : }
689 :
690 6 : GList * gncCustomerGetJoblist (const GncCustomer *cust, gboolean show_all)
691 : {
692 6 : if (!cust) return NULL;
693 :
694 6 : if (show_all)
695 : {
696 4 : return (g_list_copy (cust->jobs));
697 : }
698 : else
699 : {
700 2 : GList *list = NULL, *iterator;
701 4 : for (iterator = cust->jobs; iterator; iterator = iterator->next)
702 : {
703 2 : GncJob *j = iterator->data;
704 2 : if (gncJobGetActive (j))
705 1 : list = g_list_prepend (list, j);
706 : }
707 2 : return g_list_reverse (list);
708 : }
709 : }
710 :
711 12 : gboolean gncCustomerIsDirty (GncCustomer *cust)
712 : {
713 12 : if (!cust) return FALSE;
714 18 : return (qof_instance_is_dirty(&cust->inst) ||
715 18 : gncAddressIsDirty (cust->addr) ||
716 6 : gncAddressIsDirty (cust->shipaddr));
717 : }
718 :
719 : /* Other functions */
720 :
721 0 : int gncCustomerCompare (const GncCustomer *a, const GncCustomer *b)
722 : {
723 0 : if (!a && !b) return 0;
724 0 : if (!a && b) return 1;
725 0 : if (a && !b) return -1;
726 :
727 0 : return(strcmp(a->name, b->name));
728 : }
729 :
730 : gboolean
731 4 : gncCustomerEqual(const GncCustomer *a, const GncCustomer *b)
732 : {
733 4 : if (a == NULL && b == NULL) return TRUE;
734 4 : if (a == NULL || b == NULL) return FALSE;
735 :
736 4 : g_return_val_if_fail(GNC_IS_CUSTOMER(a), FALSE);
737 4 : g_return_val_if_fail(GNC_IS_CUSTOMER(b), FALSE);
738 :
739 4 : if (g_strcmp0(a->id, b->id) != 0)
740 : {
741 0 : PWARN("IDs differ: %s vs %s", a->id, b->id);
742 0 : return FALSE;
743 : }
744 :
745 4 : if (g_strcmp0(a->name, b->name) != 0)
746 : {
747 0 : PWARN("Names differ: %s vs %s", a->name, b->name);
748 0 : return FALSE;
749 : }
750 :
751 4 : if (g_strcmp0(a->notes, b->notes) != 0)
752 : {
753 0 : PWARN("Notes differ: %s vs %s", a->notes, b->notes);
754 0 : return FALSE;
755 : }
756 :
757 4 : if (!gncBillTermEqual(a->terms, b->terms))
758 : {
759 0 : PWARN("Bill terms differ");
760 0 : return FALSE;
761 : }
762 :
763 4 : if (!gnc_commodity_equal(a->currency, b->currency))
764 : {
765 0 : PWARN("currencies differ");
766 0 : return FALSE;
767 : }
768 :
769 4 : if (!gncTaxTableEqual(a->taxtable, b->taxtable))
770 : {
771 0 : PWARN("tax tables differ");
772 0 : return FALSE;
773 : }
774 :
775 4 : if (a->taxtable_override != b->taxtable_override)
776 : {
777 0 : PWARN("Tax table override flags differ");
778 0 : return FALSE;
779 : }
780 :
781 4 : if (a->taxincluded != b->taxincluded)
782 : {
783 0 : PWARN("Tax included flags differ");
784 0 : return FALSE;
785 : }
786 :
787 4 : if (a->active != b->active)
788 : {
789 0 : PWARN("Active flags differ");
790 0 : return FALSE;
791 : }
792 :
793 4 : if (!gncAddressEqual(a->addr, b->addr))
794 : {
795 0 : PWARN("addresses differ");
796 0 : return FALSE;
797 : }
798 4 : if (!gncAddressEqual(a->shipaddr, b->shipaddr))
799 : {
800 0 : PWARN("addresses differ");
801 0 : return FALSE;
802 : }
803 :
804 4 : if (!gnc_numeric_equal(a->credit, b->credit))
805 : {
806 0 : PWARN("Credit amounts differ");
807 0 : return FALSE;
808 : }
809 :
810 4 : if (!gnc_numeric_equal(a->discount, b->discount))
811 : {
812 0 : PWARN("Discount amounts differ");
813 0 : return FALSE;
814 : }
815 :
816 : /* FIXME: Need to check jobs list
817 : GList * jobs;
818 : */
819 :
820 4 : return TRUE;
821 : }
822 :
823 : /**
824 : * Listen for qof events.
825 : *
826 : * - If the address of a customer has changed, mark the customer as dirty.
827 : * - If a lot related to a customer has changed, clear the customer's
828 : * cached balance as it likely has become invalid.
829 : *
830 : * @param entity Entity for the event
831 : * @param event_type Event type
832 : * @param user_data User data registered with the handler
833 : * @param event_data Event data passed with the event.
834 : */
835 : static void
836 147674 : cust_handle_qof_events (QofInstance *entity, QofEventId event_type,
837 : gpointer user_data, gpointer event_data)
838 : {
839 : /* Handle address change events */
840 147674 : if ((GNC_IS_ADDRESS (entity) &&
841 68 : (event_type & QOF_EVENT_MODIFY) != 0))
842 : {
843 21 : if (GNC_IS_CUSTOMER (event_data))
844 : {
845 16 : GncCustomer* cust = GNC_CUSTOMER (event_data);
846 16 : gncCustomerBeginEdit (cust);
847 16 : mark_customer (cust);
848 16 : gncCustomerCommitEdit (cust);
849 : }
850 21 : return;
851 : }
852 :
853 : /* Handle lot change events */
854 147653 : if (GNC_IS_LOT (entity))
855 : {
856 769 : GNCLot *lot = GNC_LOT (entity);
857 : GncOwner lot_owner;
858 769 : const GncOwner *end_owner = NULL;
859 769 : GncInvoice *invoice = gncInvoiceGetInvoiceFromLot (lot);
860 :
861 : /* Determine the owner associated with the lot */
862 769 : if (invoice)
863 : /* Invoice lots */
864 408 : end_owner = gncOwnerGetEndOwner (gncInvoiceGetOwner (invoice));
865 361 : else if (gncOwnerGetOwnerFromLot (lot, &lot_owner))
866 : /* Pre-payment lots */
867 109 : end_owner = gncOwnerGetEndOwner (&lot_owner);
868 :
869 769 : if (gncOwnerGetType (end_owner) == GNC_OWNER_CUSTOMER)
870 : {
871 : /* Clear the cached balance */
872 437 : GncCustomer* cust = gncOwnerGetCustomer (end_owner);
873 437 : g_free (cust->balance);
874 437 : cust->balance = NULL;
875 : }
876 769 : return;
877 : }
878 : }
879 :
880 : /* ============================================================== */
881 : /* Package-Private functions */
882 86 : static const char * _gncCustomerPrintable (gpointer item)
883 : {
884 : // GncCustomer *c = item;
885 86 : if (!item) return "failed";
886 0 : return gncCustomerGetName((GncCustomer*)item);
887 : }
888 :
889 : static void
890 12 : destroy_customer_on_book_close(QofInstance *ent, gpointer data)
891 : {
892 12 : GncCustomer* c = GNC_CUSTOMER(ent);
893 :
894 12 : gncCustomerBeginEdit(c);
895 12 : gncCustomerDestroy(c);
896 12 : }
897 :
898 : /** Handles book end - frees all customers from the book
899 : *
900 : * @param book Book being closed
901 : */
902 : static void
903 182 : gnc_customer_book_end(QofBook* book)
904 : {
905 : QofCollection *col;
906 :
907 182 : col = qof_book_get_collection(book, GNC_ID_CUSTOMER);
908 182 : qof_collection_foreach(col, destroy_customer_on_book_close, NULL);
909 182 : }
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 86 : gboolean gncCustomerRegister (void)
927 : {
928 : static QofParam params[] =
929 : {
930 : { CUSTOMER_ID, QOF_TYPE_NATURAL, (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 86 : qof_class_register (_GNC_MOD_NAME, (QofSortFunc)gncCustomerCompare, params);
955 : /* temp */
956 86 : _gncCustomerPrintable(NULL);
957 86 : return qof_object_register (&gncCustomerDesc);
958 : }
959 :
960 0 : gchar *gncCustomerNextID (QofBook *book)
961 : {
962 0 : return qof_book_increment_and_format_counter (book, _GNC_MOD_NAME);
963 : }
964 :
965 : const gnc_numeric*
966 0 : gncCustomerGetCachedBalance (GncCustomer *cust)
967 : {
968 0 : return cust->balance;
969 : }
970 :
971 0 : void gncCustomerSetCachedBalance (GncCustomer *cust, const gnc_numeric *new_bal)
972 : {
973 0 : if (!new_bal)
974 : {
975 0 : if (cust->balance)
976 : {
977 0 : g_free (cust->balance);
978 0 : cust->balance = NULL;
979 : }
980 0 : return;
981 : }
982 :
983 0 : if (!cust->balance)
984 0 : cust->balance = g_new0 (gnc_numeric, 1);
985 :
986 0 : *cust->balance = *new_bal;
987 : }
|