Quellcode durchsuchen

Merge branch 'develop' into fix-imap-folder-append-to-not-working-issue

version-14
Suraj Shetty vor 3 Jahren
committed by GitHub
Ursprung
Commit
723bfa1ca5
Es konnte kein GPG-Schlüssel zu dieser Signatur gefunden werden GPG-Schlüssel-ID: 4AEE18F83AFDEB23
10 geänderte Dateien mit 34 neuen und 366 gelöschten Zeilen
  1. +3
    -5
      frappe/core/doctype/doctype/test_doctype.py
  2. +2
    -9
      frappe/model/document.py
  3. +11
    -107
      frappe/model/naming.py
  4. +0
    -1
      frappe/patches.txt
  5. +0
    -213
      frappe/patches/v14_0/rename_cancelled_documents.py
  6. +10
    -14
      frappe/public/js/frappe/form/form.js
  7. +0
    -6
      frappe/public/js/frappe/router.js
  8. +2
    -2
      frappe/search/full_text_search.py
  9. +5
    -9
      frappe/tests/test_naming.py
  10. +1
    -0
      frappe/translations/de.csv

+ 3
- 5
frappe/core/doctype/doctype/test_doctype.py Datei anzeigen

@@ -354,7 +354,6 @@ class TestDocType(unittest.TestCase):
dump_docs = json.dumps(docs.get('docs')) dump_docs = json.dumps(docs.get('docs'))
cancel_all_linked_docs(dump_docs) cancel_all_linked_docs(dump_docs)
data_link_doc.cancel() data_link_doc.cancel()
data_doc.name = '{}-CANC-0'.format(data_doc.name)
data_doc.load_from_db() data_doc.load_from_db()
self.assertEqual(data_link_doc.docstatus, 2) self.assertEqual(data_link_doc.docstatus, 2)
self.assertEqual(data_doc.docstatus, 2) self.assertEqual(data_doc.docstatus, 2)
@@ -378,7 +377,7 @@ class TestDocType(unittest.TestCase):
for data in link_doc.get('permissions'): for data in link_doc.get('permissions'):
data.submit = 1 data.submit = 1
data.cancel = 1 data.cancel = 1
link_doc.insert(ignore_if_duplicate=True)
link_doc.insert()


#create first parent doctype #create first parent doctype
test_doc_1 = new_doctype('Test Doctype 1') test_doc_1 = new_doctype('Test Doctype 1')
@@ -393,7 +392,7 @@ class TestDocType(unittest.TestCase):
for data in test_doc_1.get('permissions'): for data in test_doc_1.get('permissions'):
data.submit = 1 data.submit = 1
data.cancel = 1 data.cancel = 1
test_doc_1.insert(ignore_if_duplicate=True)
test_doc_1.insert()


#crete second parent doctype #crete second parent doctype
doc = new_doctype('Test Doctype 2') doc = new_doctype('Test Doctype 2')
@@ -408,7 +407,7 @@ class TestDocType(unittest.TestCase):
for data in link_doc.get('permissions'): for data in link_doc.get('permissions'):
data.submit = 1 data.submit = 1
data.cancel = 1 data.cancel = 1
doc.insert(ignore_if_duplicate=True)
doc.insert()


# create doctype data # create doctype data
data_link_doc_1 = frappe.new_doc('Test Linked Doctype 1') data_link_doc_1 = frappe.new_doc('Test Linked Doctype 1')
@@ -439,7 +438,6 @@ class TestDocType(unittest.TestCase):
# checking that doc for Test Doctype 2 is not canceled # checking that doc for Test Doctype 2 is not canceled
self.assertRaises(frappe.LinkExistsError, data_link_doc_1.cancel) self.assertRaises(frappe.LinkExistsError, data_link_doc_1.cancel)


data_doc_2.name = '{}-CANC-0'.format(data_doc_2.name)
data_doc.load_from_db() data_doc.load_from_db()
data_doc_2.load_from_db() data_doc_2.load_from_db()
self.assertEqual(data_link_doc_1.docstatus, 2) self.assertEqual(data_link_doc_1.docstatus, 2)


+ 2
- 9
frappe/model/document.py Datei anzeigen

@@ -9,7 +9,7 @@ import frappe
from frappe import _, msgprint, is_whitelisted from frappe import _, msgprint, is_whitelisted
from frappe.utils import flt, cstr, now, get_datetime_str, file_lock, date_diff from frappe.utils import flt, cstr, now, get_datetime_str, file_lock, date_diff
from frappe.model.base_document import BaseDocument, get_controller from frappe.model.base_document import BaseDocument, get_controller
from frappe.model.naming import set_new_name, gen_new_name_for_cancelled_doc
from frappe.model.naming import set_new_name
from frappe.model.docstatus import DocStatus from frappe.model.docstatus import DocStatus
from frappe.model import optional_fields, table_fields from frappe.model import optional_fields, table_fields
from frappe.model.workflow import validate_workflow from frappe.model.workflow import validate_workflow
@@ -311,9 +311,6 @@ class Document(BaseDocument):


self.check_permission("write", "save") self.check_permission("write", "save")


if self.docstatus.is_cancelled():
self._rename_doc_on_cancel()

self.set_user_and_timestamp() self.set_user_and_timestamp()
self.set_docstatus() self.set_docstatus()
self.check_if_latest() self.check_if_latest()
@@ -724,6 +721,7 @@ class Document(BaseDocument):
else: else:
tmp = frappe.db.sql("""select modified, docstatus from `tab{0}` tmp = frappe.db.sql("""select modified, docstatus from `tab{0}`
where name = %s for update""".format(self.doctype), self.name, as_dict=True) where name = %s for update""".format(self.doctype), self.name, as_dict=True)

if not tmp: if not tmp:
frappe.throw(_("Record does not exist")) frappe.throw(_("Record does not exist"))
else: else:
@@ -1376,11 +1374,6 @@ class Document(BaseDocument):
from frappe.desk.doctype.tag.tag import DocTags from frappe.desk.doctype.tag.tag import DocTags
return DocTags(self.doctype).get_tags(self.name).split(",")[1:] return DocTags(self.doctype).get_tags(self.name).split(",")[1:]


def _rename_doc_on_cancel(self):
new_name = gen_new_name_for_cancelled_doc(self)
frappe.rename_doc(self.doctype, self.name, new_name, force=True, show_alert=False)
self.name = new_name

def __repr__(self): def __repr__(self):
name = self.name or "unsaved" name = self.name or "unsaved"
doctype = self.__class__.__name__ doctype = self.__class__.__name__


+ 11
- 107
frappe/model/naming.py Datei anzeigen

@@ -1,14 +1,3 @@
"""utilities to generate a document name based on various rules defined.

NOTE:
Till version 13, whenever a submittable document is amended it's name is set to orig_name-X,
where X is a counter and it increments when amended again and so on.

From Version 14, The naming pattern is changed in a way that amended documents will
have the original name `orig_name` instead of `orig_name-X`. To make this happen
the cancelled document naming pattern is changed to 'orig_name-CANC-X'.
"""

# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: MIT. See LICENSE # License: MIT. See LICENSE


@@ -40,7 +29,7 @@ def set_new_name(doc):
doc.name = None doc.name = None


if getattr(doc, "amended_from", None): if getattr(doc, "amended_from", None):
doc.name = _get_amended_name(doc)
_set_amended_name(doc)
return return


elif getattr(doc.meta, "issingle", False): elif getattr(doc.meta, "issingle", False):
@@ -256,18 +245,6 @@ def revert_series_if_last(key, name, doc=None):
* prefix = #### and hashes = 2021 (hash doesn't exist) * prefix = #### and hashes = 2021 (hash doesn't exist)
* will search hash in key then accordingly get prefix = "" * will search hash in key then accordingly get prefix = ""
""" """
if hasattr(doc, 'amended_from'):
# Do not revert the series if the document is amended.
if doc.amended_from:
return

# Get document name by parsing incase of fist cancelled document
if doc.docstatus == 2 and not doc.amended_from:
if doc.name.endswith('-CANC'):
name, _ = NameParser.parse_docname(doc.name, sep='-CANC')
else:
name, _ = NameParser.parse_docname(doc.name, sep='-CANC-')

if ".#" in key: if ".#" in key:
prefix, hashes = key.rsplit(".", 1) prefix, hashes = key.rsplit(".", 1)
if "#" not in hashes: if "#" not in hashes:
@@ -356,9 +333,16 @@ def append_number_if_name_exists(doctype, value, fieldname="name", separator="-"
return value return value




def _get_amended_name(doc):
name, _ = NameParser(doc).parse_amended_from()
return name
def _set_amended_name(doc):
am_id = 1
am_prefix = doc.amended_from
if frappe.db.get_value(doc.doctype, doc.amended_from, "amended_from"):
am_id = cint(doc.amended_from.split("-")[-1]) + 1
am_prefix = "-".join(doc.amended_from.split("-")[:-1]) # except the last hyphen

doc.name = am_prefix + "-" + str(am_id)
return doc.name



def _field_autoname(autoname, doc, skip_slicing=None): def _field_autoname(autoname, doc, skip_slicing=None):
""" """
@@ -399,83 +383,3 @@ def _format_autoname(autoname, doc):
name = re.sub(r"(\{[\w | #]+\})", get_param_value_for_match, autoname_value) name = re.sub(r"(\{[\w | #]+\})", get_param_value_for_match, autoname_value)


return name return name

class NameParser:
"""Parse document name and return parts of it.

NOTE: It handles cancellend and amended doc parsing for now. It can be expanded.
"""
def __init__(self, doc):
self.doc = doc

def parse_amended_from(self):
"""
Cancelled document naming will be in one of these formats

* original_name-X-CANC - This is introduced to migrate old style naming to new style
* original_name-CANC - This is introduced to migrate old style naming to new style
* original_name-CANC-X - This is the new style naming

New style naming: In new style naming amended documents will have original name. That says,
when a document gets cancelled we need rename the document by adding `-CANC-X` to the end
so that amended documents can use the original name.

Old style naming: cancelled documents stay with original name and when amended, amended one
gets a new name as `original_name-X`. To bring new style naming we had to change the existing
cancelled document names and that is done by adding `-CANC` to cancelled documents through patch.
"""
if not getattr(self.doc, 'amended_from', None):
return (None, None)

# Handle old style cancelled documents (original_name-X-CANC, original_name-CANC)
if self.doc.amended_from.endswith('-CANC'):
name, _ = self.parse_docname(self.doc.amended_from, '-CANC')
amended_from_doc = frappe.get_all(
self.doc.doctype,
filters = {'name': self.doc.amended_from},
fields = ['amended_from'],
limit=1)

# Handle format original_name-X-CANC.
if amended_from_doc and amended_from_doc[0].amended_from:
return self.parse_docname(name, '-')
return name, None

# Handle new style cancelled documents
return self.parse_docname(self.doc.amended_from, '-CANC-')

@classmethod
def parse_docname(cls, name, sep='-'):
split_list = name.rsplit(sep, 1)

if len(split_list) == 1:
return (name, None)
return (split_list[0], split_list[1])

def get_cancelled_doc_latest_counter(tname, docname):
"""Get the latest counter used for cancelled docs of given docname.
"""
name_prefix = f'{docname}-CANC-'

rows = frappe.db.sql("""
select
name
from `tab{tname}`
where
name like %(name_prefix)s and docstatus=2
""".format(tname=tname), {'name_prefix': name_prefix+'%'}, as_dict=1)

if not rows:
return -1
return max([int(row.name.replace(name_prefix, '') or -1) for row in rows])

def gen_new_name_for_cancelled_doc(doc):
"""Generate a new name for cancelled document.
"""
if getattr(doc, "amended_from", None):
name, _ = NameParser(doc).parse_amended_from()
else:
name = doc.name

counter = get_cancelled_doc_latest_counter(doc.doctype, name)
return f'{name}-CANC-{counter+1}'

+ 0
- 1
frappe/patches.txt Datei anzeigen

@@ -185,7 +185,6 @@ frappe.patches.v13_0.queryreport_columns
frappe.patches.v13_0.jinja_hook frappe.patches.v13_0.jinja_hook
frappe.patches.v13_0.update_notification_channel_if_empty frappe.patches.v13_0.update_notification_channel_if_empty
frappe.patches.v13_0.set_first_day_of_the_week frappe.patches.v13_0.set_first_day_of_the_week
frappe.patches.v14_0.rename_cancelled_documents
frappe.patches.v14_0.update_workspace2 # 20.09.2021 frappe.patches.v14_0.update_workspace2 # 20.09.2021
frappe.patches.v14_0.save_ratings_in_fraction #23-12-2021 frappe.patches.v14_0.save_ratings_in_fraction #23-12-2021
frappe.patches.v14_0.transform_todo_schema frappe.patches.v14_0.transform_todo_schema


+ 0
- 213
frappe/patches/v14_0/rename_cancelled_documents.py Datei anzeigen

@@ -1,213 +0,0 @@
import functools
import traceback

import frappe

def execute():
"""Rename cancelled documents by adding a postfix.
"""
rename_cancelled_docs()

def get_submittable_doctypes():
"""Returns list of submittable doctypes in the system.
"""
return frappe.db.get_all('DocType', filters={'is_submittable': 1}, pluck='name')

def get_cancelled_doc_names(doctype):
"""Return names of cancelled document names those are in old format.
"""
docs = frappe.db.get_all(doctype, filters={'docstatus': 2}, pluck='name')
return [each for each in docs if not (each.endswith('-CANC') or ('-CANC-' in each))]

@functools.lru_cache()
def get_linked_doctypes():
"""Returns list of doctypes those are linked with given doctype using 'Link' fieldtype.
"""
filters=[['fieldtype','=', 'Link']]
links = frappe.get_all("DocField",
fields=["parent", "fieldname", "options as linked_to"],
filters=filters,
as_list=1)

links+= frappe.get_all("Custom Field",
fields=["dt as parent", "fieldname", "options as linked_to"],
filters=filters,
as_list=1)

links_by_doctype = {}
for doctype, fieldname, linked_to in links:
links_by_doctype.setdefault(linked_to, []).append((doctype, fieldname))
return links_by_doctype

@functools.lru_cache()
def get_single_doctypes():
return frappe.get_all("DocType", filters={'issingle': 1}, pluck='name')

@functools.lru_cache()
def get_dynamic_linked_doctypes():
filters=[['fieldtype','=', 'Dynamic Link']]

# find dynamic links of parents
links = frappe.get_all("DocField",
fields=["parent as doctype", "fieldname", "options as doctype_fieldname"],
filters=filters,
as_list=1)
links+= frappe.get_all("Custom Field",
fields=["dt as doctype", "fieldname", "options as doctype_fieldname"],
filters=filters,
as_list=1)
return links

@functools.lru_cache()
def get_child_tables():
"""
"""
filters =[['fieldtype', 'in', ('Table', 'Table MultiSelect')]]
links = frappe.get_all("DocField",
fields=["parent as doctype", "options as child_table"],
filters=filters,
as_list=1)

links+= frappe.get_all("Custom Field",
fields=["dt as doctype", "options as child_table"],
filters=filters,
as_list=1)

map = {}
for doctype, child_table in links:
map.setdefault(doctype, []).append(child_table)
return map

def update_cancelled_document_names(doctype, cancelled_doc_names):
return frappe.db.sql("""
update
`tab{doctype}`
set
name=CONCAT(name, '-CANC')
where
docstatus=2
and
name in %(cancelled_doc_names)s;
""".format(doctype=doctype), {'cancelled_doc_names': cancelled_doc_names})

def update_amended_field(doctype, cancelled_doc_names):
return frappe.db.sql("""
update
`tab{doctype}`
set
amended_from=CONCAT(amended_from, '-CANC')
where
amended_from in %(cancelled_doc_names)s;
""".format(doctype=doctype), {'cancelled_doc_names': cancelled_doc_names})

def update_attachments(doctype, cancelled_doc_names):
frappe.db.sql("""
update
`tabFile`
set
attached_to_name=CONCAT(attached_to_name, '-CANC')
where
attached_to_doctype=%(dt)s and attached_to_name in %(cancelled_doc_names)s
""", {'cancelled_doc_names': cancelled_doc_names, 'dt': doctype})

def update_versions(doctype, cancelled_doc_names):
frappe.db.sql("""
UPDATE
`tabVersion`
SET
docname=CONCAT(docname, '-CANC')
WHERE
ref_doctype=%(dt)s AND docname in %(cancelled_doc_names)s
""", {'cancelled_doc_names': cancelled_doc_names, 'dt': doctype})

def update_linked_doctypes(doctype, cancelled_doc_names):
single_doctypes = get_single_doctypes()

for linked_dt, field in get_linked_doctypes().get(doctype, []):
if linked_dt not in single_doctypes:
frappe.db.sql("""
update
`tab{linked_dt}`
set
`{column}`=CONCAT(`{column}`, '-CANC')
where
`{column}` in %(cancelled_doc_names)s;
""".format(linked_dt=linked_dt, column=field),
{'cancelled_doc_names': cancelled_doc_names})
else:
doc = frappe.get_single(linked_dt)
if getattr(doc, field) in cancelled_doc_names:
setattr(doc, field, getattr(doc, field)+'-CANC')
doc.flags.ignore_mandatory=True
doc.flags.ignore_validate=True
doc.save(ignore_permissions=True)

def update_dynamic_linked_doctypes(doctype, cancelled_doc_names):
single_doctypes = get_single_doctypes()

for linked_dt, fieldname, doctype_fieldname in get_dynamic_linked_doctypes():
if linked_dt not in single_doctypes:
frappe.db.sql("""
update
`tab{linked_dt}`
set
`{column}`=CONCAT(`{column}`, '-CANC')
where
`{column}` in %(cancelled_doc_names)s and {doctype_fieldname}=%(dt)s;
""".format(linked_dt=linked_dt, column=fieldname, doctype_fieldname=doctype_fieldname),
{'cancelled_doc_names': cancelled_doc_names, 'dt': doctype})
else:
doc = frappe.get_single(linked_dt)
if getattr(doc, doctype_fieldname) == doctype and getattr(doc, fieldname) in cancelled_doc_names:
setattr(doc, fieldname, getattr(doc, fieldname)+'-CANC')
doc.flags.ignore_mandatory=True
doc.flags.ignore_validate=True
doc.save(ignore_permissions=True)

def update_child_tables(doctype, cancelled_doc_names):
child_tables = get_child_tables().get(doctype, [])
single_doctypes = get_single_doctypes()

for table in child_tables:
if table not in single_doctypes:
frappe.db.sql("""
update
`tab{table}`
set
parent=CONCAT(parent, '-CANC')
where
parenttype=%(dt)s and parent in %(cancelled_doc_names)s;
""".format(table=table), {'cancelled_doc_names': cancelled_doc_names, 'dt': doctype})
else:
doc = frappe.get_single(table)
if getattr(doc, 'parenttype')==doctype and getattr(doc, 'parent') in cancelled_doc_names:
setattr(doc, 'parent', getattr(doc, 'parent')+'-CANC')
doc.flags.ignore_mandatory=True
doc.flags.ignore_validate=True
doc.save(ignore_permissions=True)

def rename_cancelled_docs():
submittable_doctypes = get_submittable_doctypes()

for dt in submittable_doctypes:
for retry in range(2):
try:
cancelled_doc_names = tuple(get_cancelled_doc_names(dt))
if not cancelled_doc_names:
break
update_cancelled_document_names(dt, cancelled_doc_names)
update_amended_field(dt, cancelled_doc_names)
update_child_tables(dt, cancelled_doc_names)
update_linked_doctypes(dt, cancelled_doc_names)
update_dynamic_linked_doctypes(dt, cancelled_doc_names)
update_attachments(dt, cancelled_doc_names)
update_versions(dt, cancelled_doc_names)
print(f"Renaming cancelled records of {dt} doctype")
frappe.db.commit()
break
except Exception:
if retry == 1:
print(f"Failed to rename the cancelled records of {dt} doctype, moving on!")
traceback.print_exc()
frappe.db.rollback()


+ 10
- 14
frappe/public/js/frappe/form/form.js Datei anzeigen

@@ -860,36 +860,32 @@ frappe.ui.form.Form = class FrappeForm {
} }


_cancel(btn, callback, on_error, skip_confirm) { _cancel(btn, callback, on_error, skip_confirm) {
const me = this;
const cancel_doc = () => { const cancel_doc = () => {
frappe.validated = true; frappe.validated = true;
this.script_manager.trigger("before_cancel").then(() => {
me.script_manager.trigger("before_cancel").then(() => {
if (!frappe.validated) { if (!frappe.validated) {
return this.handle_save_fail(btn, on_error);
return me.handle_save_fail(btn, on_error);
} }


const original_name = this.docname;
const after_cancel = (r) => {
var after_cancel = function(r) {
if (r.exc) { if (r.exc) {
this.handle_save_fail(btn, on_error);
me.handle_save_fail(btn, on_error);
} else { } else {
frappe.utils.play_sound("cancel"); frappe.utils.play_sound("cancel");
me.refresh();
callback && callback(); callback && callback();
this.script_manager.trigger("after_cancel");
frappe.run_serially([
() => this.rename_notify(this.doctype, original_name, r.docs[0].name),
() => frappe.router.clear_re_route(this.doctype, original_name),
() => this.refresh(),
]);
me.script_manager.trigger("after_cancel");
} }
}; };
frappe.ui.form.save(this, "cancel", after_cancel, btn);
frappe.ui.form.save(me, "cancel", after_cancel, btn);
}); });
} }


if (skip_confirm) { if (skip_confirm) {
cancel_doc(); cancel_doc();
} else { } else {
frappe.confirm(__("Permanently Cancel {0}?", [this.docname]), cancel_doc, this.handle_save_fail(btn, on_error));
frappe.confirm(__("Permanently Cancel {0}?", [this.docname]), cancel_doc, me.handle_save_fail(btn, on_error));
} }
}; };


@@ -911,7 +907,7 @@ frappe.ui.form.Form = class FrappeForm {
'docname': this.doc.name 'docname': this.doc.name
}).then(is_amended => { }).then(is_amended => {
if (is_amended) { if (is_amended) {
frappe.throw(__('This document is already amended, you cannot amend it again'));
frappe.throw(__('This document is already amended, you cannot ammend it again'));
} }
this.validate_form_action("Amend"); this.validate_form_action("Amend");
var me = this; var me = this;


+ 0
- 6
frappe/public/js/frappe/router.js Datei anzeigen

@@ -250,12 +250,6 @@ frappe.router = {
} }
}, },


clear_re_route(doctype, docname) {
delete frappe.re_route[
`${encodeURIComponent(frappe.router.slug(doctype))}/${encodeURIComponent(docname)}`
];
},

set_title(sub_path) { set_title(sub_path) {
if (frappe.route_titles[sub_path]) { if (frappe.route_titles[sub_path]) {
frappe.utils.set_title(frappe.route_titles[sub_path]); frappe.utils.set_title(frappe.route_titles[sub_path]);


+ 2
- 2
frappe/search/full_text_search.py Datei anzeigen

@@ -66,7 +66,7 @@ class FullTextSearch:


ix = self.get_index() ix = self.get_index()
with ix.searcher(): with ix.searcher():
writer = ix.writer()
writer = AsyncWriter(ix)
writer.delete_by_term(self.id, doc_name) writer.delete_by_term(self.id, doc_name)
writer.commit(optimize=True) writer.commit(optimize=True)


@@ -98,7 +98,7 @@ class FullTextSearch:
def build_index(self): def build_index(self):
"""Build index for all parsed documents""" """Build index for all parsed documents"""
ix = self.create_index() ix = self.create_index()
writer = ix.writer()
writer = AsyncWriter(ix)


for i, document in enumerate(self.documents): for i, document in enumerate(self.documents):
if document: if document:


+ 5
- 9
frappe/tests/test_naming.py Datei anzeigen

@@ -144,6 +144,7 @@ class TestNaming(unittest.TestCase):
current_index = frappe.db.sql("""SELECT current from `tabSeries` where name = %s""", series, as_dict=True)[0] current_index = frappe.db.sql("""SELECT current from `tabSeries` where name = %s""", series, as_dict=True)[0]


self.assertEqual(current_index.get('current'), 2) self.assertEqual(current_index.get('current'), 2)

frappe.db.delete("Series", {"name": series}) frappe.db.delete("Series", {"name": series})


def test_naming_for_cancelled_and_amended_doc(self): def test_naming_for_cancelled_and_amended_doc(self):
@@ -166,25 +167,20 @@ class TestNaming(unittest.TestCase):
doc.submit() doc.submit()
doc.cancel() doc.cancel()
cancelled_name = doc.name cancelled_name = doc.name
self.assertEqual(cancelled_name, "{}-CANC-0".format(original_name))
self.assertEqual(cancelled_name, original_name)


amended_doc = frappe.copy_doc(doc) amended_doc = frappe.copy_doc(doc)
amended_doc.docstatus = 0 amended_doc.docstatus = 0
amended_doc.amended_from = doc.name amended_doc.amended_from = doc.name
amended_doc.save() amended_doc.save()
self.assertEqual(amended_doc.name, original_name)
self.assertEqual(amended_doc.name, "{}-1".format(original_name))


amended_doc.submit() amended_doc.submit()
amended_doc.cancel() amended_doc.cancel()
self.assertEqual(amended_doc.name, "{}-CANC-1".format(original_name))
self.assertEqual(amended_doc.name, "{}-1".format(original_name))


submittable_doctype.delete() submittable_doctype.delete()


def test_parse_naming_series_for_consecutive_week_number(self):
week = determine_consecutive_week_number(now_datetime())
name = parse_naming_series('PREFIX-.WW.-SUFFIX')
expected_name = 'PREFIX-{}-SUFFIX'.format(week)
self.assertEqual(name, expected_name)


def test_determine_consecutive_week_number(self): def test_determine_consecutive_week_number(self):
from datetime import datetime from datetime import datetime
@@ -207,4 +203,4 @@ class TestNaming(unittest.TestCase):


dt = datetime.fromisoformat("2021-12-31") dt = datetime.fromisoformat("2021-12-31")
w = determine_consecutive_week_number(dt) w = determine_consecutive_week_number(dt)
self.assertEqual(w, "52")
self.assertEqual(w, "52")

+ 1
- 0
frappe/translations/de.csv Datei anzeigen

@@ -151,6 +151,7 @@ My Account,Mein Konto,
New Address,Neue Adresse, New Address,Neue Adresse,
New Contact,Neuer Kontakt, New Contact,Neuer Kontakt,
Next,Weiter, Next,Weiter,
No,Nein,
No Data,Keine Daten, No Data,Keine Daten,
No address added yet.,Noch keine Adresse hinzugefügt., No address added yet.,Noch keine Adresse hinzugefügt.,
No contacts added yet.,Noch keine Kontakte hinzugefügt., No contacts added yet.,Noch keine Kontakte hinzugefügt.,


Laden…
Abbrechen
Speichern