mirror of
https://github.com/pretix/pretix.git
synced 2026-10-30 23:59:51 +00:00
Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
640b822f33 | ||
|
|
8e0b490964 |
No files matched your search
@@ -19,4 +19,4 @@
|
||||
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
|
||||
# <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
__version__ = "2026.10.0.dev0"
|
||||
__version__ = "2026.9.0.dev0"
|
||||
@@ -822,6 +822,7 @@ class EventSettingsSerializer(SettingsSerializer):
|
||||
'invoice_address_not_asked_free',
|
||||
'invoice_show_payments',
|
||||
'invoice_reissue_after_modify',
|
||||
'invoice_transmit_after_modify',
|
||||
'invoice_include_free',
|
||||
'invoice_generate',
|
||||
'invoice_generate_only_business',
|
||||
|
||||
@@ -151,29 +151,12 @@ class LoggingMixin:
|
||||
logentry = LogEntry(content_object=self, user=user, action_type=action, event=event,
|
||||
organizer_id=organizer_id, **kwargs)
|
||||
if isinstance(data, dict):
|
||||
# sensitivekeys are matched whether contained, not exactly
|
||||
sensitivekeys = [
|
||||
'auth',
|
||||
'key',
|
||||
'token',
|
||||
'password',
|
||||
'Password',
|
||||
'_pass',
|
||||
'salt',
|
||||
'secret',
|
||||
]
|
||||
sensitivekeys = ['password', 'secret', 'api_key']
|
||||
|
||||
def clean_recursive(d):
|
||||
data = d
|
||||
data_iterator = d.items() if isinstance(d, dict) else enumerate(d)
|
||||
for k, v in data_iterator:
|
||||
if v and any([sensitivekey in str(k) for sensitivekey in sensitivekeys]):
|
||||
for sensitivekey in sensitivekeys:
|
||||
for k, v in data.items():
|
||||
if (sensitivekey in k) and v:
|
||||
data[k] = "********"
|
||||
elif isinstance(v, (list, dict)):
|
||||
data[k] = clean_recursive(v)
|
||||
return data
|
||||
|
||||
data = clean_recursive(data)
|
||||
|
||||
logentry.data = json.dumps(data, cls=CustomJSONEncoder, sort_keys=True)
|
||||
elif data:
|
||||
|
||||
@@ -1299,6 +1299,20 @@ DEFAULTS = {
|
||||
"changes made through the backend."),
|
||||
)
|
||||
},
|
||||
'invoice_transmit_after_modify': {
|
||||
'default': 'False',
|
||||
'type': bool,
|
||||
'form_class': forms.BooleanField,
|
||||
'serializer_class': serializers.BooleanField,
|
||||
'write_permission': 'event.settings.invoicing:write',
|
||||
'form_kwargs': dict(
|
||||
label=_("Automatically transmit cancellation and reissued invoice on address changes"),
|
||||
help_text=_("By default, the cancellation and the reissued invoice are not transmitted after a change "
|
||||
"from the customer. This option only affects transmission methods which do not enforce "
|
||||
"automatic transmission of invoices."),
|
||||
widget=forms.CheckboxInput(attrs={'data-checkbox-dependency': '#id_invoice_reissue_after_modify'}),
|
||||
)
|
||||
},
|
||||
'invoice_regenerate_allowed': {
|
||||
'default': 'False',
|
||||
'type': bool,
|
||||
|
||||
@@ -1060,6 +1060,7 @@ class InvoiceSettingsForm(EventSettingsValidationMixin, SettingsForm):
|
||||
'invoice_include_free',
|
||||
'invoice_show_payments',
|
||||
'invoice_reissue_after_modify',
|
||||
'invoice_transmit_after_modify',
|
||||
'invoice_generate',
|
||||
'invoice_generate_only_business',
|
||||
'invoice_period',
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
{% bootstrap_field form.invoice_include_free layout="control" %}
|
||||
{% bootstrap_field form.invoice_show_payments layout="control" %}
|
||||
{% bootstrap_field form.invoice_reissue_after_modify layout="control" %}
|
||||
{% bootstrap_field form.invoice_transmit_after_modify layout="control" %}
|
||||
{% bootstrap_field form.invoice_numbers_consecutive layout="control" %}
|
||||
{% bootstrap_field form.invoice_numbers_prefix layout="control" %}
|
||||
{% bootstrap_field form.invoice_numbers_prefix_cancellations layout="control" %}
|
||||
|
||||
@@ -76,7 +76,7 @@ from pretix.base.models.tax import TaxedPrice
|
||||
from pretix.base.payment import PaymentException
|
||||
from pretix.base.services.invoices import (
|
||||
generate_cancellation, generate_invoice, invoice_pdf, invoice_pdf_task,
|
||||
invoice_qualified,
|
||||
invoice_qualified, transmit_invoice,
|
||||
)
|
||||
from pretix.base.services.orders import (
|
||||
OrderChangeManager, OrderError, _try_auto_refund, cancel_order,
|
||||
@@ -907,6 +907,10 @@ class OrderModify(EventViewMixin, OrderDetailMixin, OrderQuestionsViewMixin, Tem
|
||||
self.order.log_action('pretix.event.order.invoice.reissued', data={
|
||||
'invoice': inv.pk
|
||||
})
|
||||
if self.request.event.settings.invoice_transmit_after_modify:
|
||||
transmit_invoice.apply_async(args=(c.event_id, c.pk, True))
|
||||
if c.pk != inv.pk:
|
||||
transmit_invoice.apply_async(args=(inv.event_id, inv.pk, True))
|
||||
messages.success(self.request, _('The invoice has been reissued.'))
|
||||
except Exception as e:
|
||||
self.order.log_action("pretix.event.order.invoice.failed", data={
|
||||
|
||||
@@ -94,83 +94,6 @@ def question(event, item):
|
||||
return q
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_log_action_filter(event, order):
|
||||
sensitivekeys = [
|
||||
'auth',
|
||||
'key',
|
||||
'token',
|
||||
'password',
|
||||
'Password',
|
||||
'_pass',
|
||||
'salt',
|
||||
'secret',
|
||||
]
|
||||
log_data = log_data_filtered = {
|
||||
'foo': 'bar',
|
||||
}
|
||||
for d in (log_data, log_data_filtered):
|
||||
value = '********' if d == log_data_filtered else '__sensitive_info__'
|
||||
for k in sensitivekeys:
|
||||
d[k] = d['prefix' + k] = d['prefix' + k + 'suffix'] = d[k + 'suffix'] = value
|
||||
|
||||
d['recursive_dict'] = {
|
||||
'test': 'dummy',
|
||||
'test_token': value,
|
||||
'd': {
|
||||
'baz': 'foo',
|
||||
'mypassword': value,
|
||||
'l': [
|
||||
'foo',
|
||||
'bar',
|
||||
{
|
||||
'baz': True,
|
||||
'superkey': value,
|
||||
},
|
||||
],
|
||||
},
|
||||
'l': [
|
||||
'okay',
|
||||
{
|
||||
'salt_to_secret': value,
|
||||
'name': 'okay'
|
||||
},
|
||||
'element',
|
||||
[
|
||||
'another',
|
||||
{
|
||||
'api_password': value,
|
||||
},
|
||||
'list'
|
||||
]
|
||||
]
|
||||
}
|
||||
d['recursive_list'] = [
|
||||
'foo',
|
||||
'bar',
|
||||
[
|
||||
'foo',
|
||||
'bar',
|
||||
{
|
||||
'foo': 'bar',
|
||||
'api_token': value,
|
||||
},
|
||||
'baz',
|
||||
],
|
||||
{
|
||||
'foo': 'bar',
|
||||
'MyPassword': value,
|
||||
}
|
||||
]
|
||||
l1 = order.log_action(
|
||||
'dummy.log',
|
||||
data=log_data,
|
||||
)
|
||||
|
||||
l1.refresh_from_db()
|
||||
assert json.loads(l1.data) == log_data_filtered
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_email_shredder(event, order):
|
||||
l1 = order.log_action(
|
||||
|
||||
@@ -405,6 +405,7 @@ class OrdersTest(BaseOrdersTest):
|
||||
|
||||
def test_modify_invoice_regenerate(self):
|
||||
self.event.settings.set('invoice_reissue_after_modify', True)
|
||||
self.event.settings.set('invoice_transmit_after_modify', False)
|
||||
self.event.settings.set('invoice_address_asked', True)
|
||||
with scopes_disabled():
|
||||
generate_invoice(self.order)
|
||||
@@ -422,40 +423,68 @@ class OrdersTest(BaseOrdersTest):
|
||||
with scopes_disabled():
|
||||
assert self.order.invoices.count() == 1
|
||||
|
||||
response = self.client.post(
|
||||
'/%s/%s/order/%s/%s/modify' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret), {
|
||||
'%s-question_%s' % (self.ticket_pos.id, self.question.id): 'ABC',
|
||||
'zipcode': '12345',
|
||||
'street': 'Main Street',
|
||||
'city': 'Heidelberg',
|
||||
'country': 'DE',
|
||||
'transmission_type': 'email',
|
||||
}, follow=True)
|
||||
self.assertRedirects(response,
|
||||
'/%s/%s/order/%s/%s/' % (self.orga.slug, self.event.slug, self.order.code,
|
||||
self.order.secret),
|
||||
target_status_code=200)
|
||||
with self.captureOnCommitCallbacks(execute=True):
|
||||
response = self.client.post(
|
||||
'/%s/%s/order/%s/%s/modify' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret), {
|
||||
'%s-question_%s' % (self.ticket_pos.id, self.question.id): 'ABC',
|
||||
'zipcode': '12345',
|
||||
'street': 'Main Street',
|
||||
'city': 'Heidelberg',
|
||||
'country': 'DE',
|
||||
'transmission_type': 'email',
|
||||
}, follow=True)
|
||||
self.assertRedirects(response,
|
||||
'/%s/%s/order/%s/%s/' % (self.orga.slug, self.event.slug, self.order.code,
|
||||
self.order.secret),
|
||||
target_status_code=200)
|
||||
with scopes_disabled():
|
||||
assert self.order.invoices.count() == 3
|
||||
for inv in self.order.invoices.all():
|
||||
assert inv.transmission_status == Invoice.TRANSMISSION_STATUS_PENDING
|
||||
|
||||
self.event.settings.set('invoice_reissue_after_modify', False)
|
||||
|
||||
response = self.client.post(
|
||||
'/%s/%s/order/%s/%s/modify' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret), {
|
||||
'%s-question_%s' % (self.ticket_pos.id, self.question.id): 'ABC',
|
||||
'zipcode': '54321',
|
||||
'street': 'Main Street',
|
||||
'city': 'Heidelberg',
|
||||
'country': 'DE',
|
||||
'transmission_type': 'email',
|
||||
}, follow=True)
|
||||
self.assertRedirects(response,
|
||||
'/%s/%s/order/%s/%s/' % (self.orga.slug, self.event.slug, self.order.code,
|
||||
self.order.secret),
|
||||
target_status_code=200)
|
||||
with self.captureOnCommitCallbacks(execute=True):
|
||||
response = self.client.post(
|
||||
'/%s/%s/order/%s/%s/modify' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret), {
|
||||
'%s-question_%s' % (self.ticket_pos.id, self.question.id): 'ABC',
|
||||
'zipcode': '54321',
|
||||
'street': 'Main Street',
|
||||
'city': 'Heidelberg',
|
||||
'country': 'DE',
|
||||
'transmission_type': 'email',
|
||||
}, follow=True)
|
||||
self.assertRedirects(response,
|
||||
'/%s/%s/order/%s/%s/' % (self.orga.slug, self.event.slug, self.order.code,
|
||||
self.order.secret),
|
||||
target_status_code=200)
|
||||
with scopes_disabled():
|
||||
assert self.order.invoices.count() == 3
|
||||
|
||||
self.event.settings.set('invoice_reissue_after_modify', True)
|
||||
self.event.settings.set('invoice_transmit_after_modify', True)
|
||||
|
||||
with self.captureOnCommitCallbacks(execute=True):
|
||||
response = self.client.post(
|
||||
'/%s/%s/order/%s/%s/modify' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret), {
|
||||
'%s-question_%s' % (self.ticket_pos.id, self.question.id): 'ABC',
|
||||
'zipcode': '12345',
|
||||
'street': 'Main Street',
|
||||
'city': 'Heidelberg',
|
||||
'country': 'DE',
|
||||
'transmission_type': 'email',
|
||||
}, follow=True)
|
||||
self.assertRedirects(response,
|
||||
'/%s/%s/order/%s/%s/' % (self.orga.slug, self.event.slug, self.order.code,
|
||||
self.order.secret),
|
||||
target_status_code=200)
|
||||
|
||||
with scopes_disabled():
|
||||
assert self.order.invoices.count() == 5
|
||||
last_two = self.order.invoices.order_by('-pk')[:2]
|
||||
for inv in last_two:
|
||||
assert inv.transmission_status != Invoice.TRANSMISSION_STATUS_PENDING
|
||||
|
||||
def test_orders_attendee_modify_invalid(self):
|
||||
self.order.status = Order.STATUS_CANCELED
|
||||
self.order.save()
|
||||
|
||||
Reference in new issue
Block a user